diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000000..75e71bdd7c1b
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,22 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/go
+{
+ "name": "Go",
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
+ "image": "mcr.microsoft.com/devcontainers/go:1.26-bookworm",
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ "features": {
+ "ghcr.io/devcontainers/features/docker-in-docker:2": {},
+ "ghcr.io/devcontainers/features/github-cli:1": {},
+ "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
+ },
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ "forwardPorts": [
+ 2379,
+ 2380
+ ],
+ // Use 'postCreateCommand' to run commands after the container is created.
+ "postCreateCommand": "make build"
+ // Configure tool-specific properties.
+ // "customizations": {},
+}
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 5ba33bc16bc7..a7cfc14dc566 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -10,7 +10,7 @@ body:
label: Bug report criteria
description: Please confirm this bug report meets the following criteria.
options:
- - label: This bug report is not security related, security issues should be disclosed privately via [etcd maintainers](mailto:etcd-maintainers@googlegroups.com).
+ - label: This bug report is not security related, security issues should be disclosed privately via security@etcd.io.
- label: This is not a support request or question, support requests or questions should be raised in the etcd [discussion forums](https://github.com/etcd-io/etcd/discussions).
- label: You have read the etcd [bug reporting guidelines](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md).
- label: Existing open issues along with etcd [frequently asked questions](https://etcd.io/docs/latest/faq) have been checked and this is not a duplicate.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 48dc1a253d48..43b38bbfe909 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,2 +1,6 @@
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6931f34066e7..21b563da9d5e 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -36,3 +36,14 @@ updates:
target-branch: "release-3.5"
schedule:
interval: monthly
+
+ - package-ecosystem: docker
+ directory: /
+ target-branch: "release-3.6"
+ schedule:
+ interval: monthly
+
+ - package-ecosystem: docker
+ directory: /tools/container-images/devcontainer
+ schedule:
+ interval: weekly
diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index 1c04a756b9ab..000000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,56 +0,0 @@
----
-# Configuration for probot-stale - https://github.com/probot/stale
-
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 90
-
-# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 21
-
-# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
-onlyLabels: []
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
- - "stage/tracked"
-
-# Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
-
-# Set to true to ignore issues in a milestone (defaults to false)
-exemptMilestones: false
-
-# Set to true to ignore issues with an assignee (defaults to false)
-exemptAssignees: false
-
-# Label to use when marking as stale
-staleLabel: stale
-
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.
-# Comment to post when removing the stale label.
-# unmarkComment: >
-# Your comment here.
-
-# Comment to post when closing a stale Issue or Pull Request.
-# closeComment: >
-# Your comment here.
-
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 30
-
-# Limit to only `issues` or `pulls`
-# only: issues
-
-# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
-# pulls:
-# daysUntilStale: 30
-# markComment: >
-# This pull request has been automatically marked as stale because it has not had
-# recent activity. It will be closed if no further activity occurs. Thank you
-# for your contributions.
-
-# issues:
-# exemptLabels:
-# - confirmed
diff --git a/.github/workflows/antithesis-test.yml b/.github/workflows/antithesis-test.yml
new file mode 100644
index 000000000000..804a35a59c10
--- /dev/null
+++ b/.github/workflows/antithesis-test.yml
@@ -0,0 +1,93 @@
+---
+name: Build and trigger Antithesis exploration
+
+on:
+ # pull_request:
+ # branches: [main]
+ schedule:
+ - cron: "0 0 * * *" # run every day at midnight
+ workflow_dispatch:
+ inputs:
+ test:
+ description: 'Test name'
+ required: false
+ type: string
+ duration:
+ description: 'Duration (exploration hours)'
+ required: true
+ type: int
+ description:
+ description: 'Description (avoid quotes, please!)'
+ required: true
+ type: string
+ etcd_ref:
+ description: 'etcd version to build etcd-server from'
+ required: false
+ type: string
+ email:
+ description: 'Additional email notification recipient (separate with ;)'
+ required: true
+ type: string
+
+# Declare default permissions as read only.
+permissions: read-all
+
+env:
+ REGISTRY: us-central1-docker.pkg.dev
+ REPOSITORY: molten-verve-216720/linuxfoundation-repository
+
+jobs:
+ build-and-push-and-test:
+ runs-on: ubuntu-latest
+ environment: Antithesis
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - name: Login to Antithesis Docker Registry
+ uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: _json_key
+ password: ${{ secrets.ANTITHESIS_CONTAINER_REGISTRY_TOKEN }}
+
+ - name: Build and push config image
+ working-directory: ./tests/antithesis
+ run: |
+ make antithesis-build-config-image IMAGE_TAG=${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
+ export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
+ docker tag etcd-config:latest $IMAGE
+ docker push $IMAGE
+
+ - name: Build and push client image
+ working-directory: ./tests/antithesis
+ run: |
+ make antithesis-build-client-docker-image
+ export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-client:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
+ docker tag etcd-client:latest $IMAGE
+ docker push $IMAGE
+
+ - name: Build and push etcd image
+ working-directory: ./tests/antithesis
+ run: |
+ make antithesis-build-etcd-image REF=${{ inputs.etcd_ref || 'main' }}
+ export IMAGE="${{ env.REGISTRY }}/${{ env.REPOSITORY }}/etcd-server:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}"
+ docker tag etcd-server:latest $IMAGE
+ docker push $IMAGE
+
+ - name: Run Antithesis Tests
+ uses: antithesishq/antithesis-trigger-action@72e1bc49d6a5f66c9b892e2b311844e00d342554 # v0.11
+ with:
+ notebook_name: etcd
+ tenant: linuxfoundation
+ username: ${{ secrets.ANTITHESIS_WEBHOOK_USERNAME }}
+ password: ${{ secrets.ANTITHESIS_WEBHOOK_PASSWORD }}
+ github_token: ${{ secrets.GH_PAT }}
+ config_image: us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
+ images: us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-client:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }};us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-server:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }};docker.io/library/ubuntu:latest;us-central1-docker.pkg.dev/molten-verve-216720/linuxfoundation-repository/etcd-config:${{ inputs.etcd_ref || 'main' }}_${{ github.sha }}
+ description: ${{ inputs.description || 'etcd nightly antithesis run' }}
+ email_recipients: ${{ inputs.email || 'siarkowicz@google.com' }}
+ test_name: ${{ inputs.test || 'etcd nightly antithesis run' }}
+ additional_parameters: |-
+ custom.duration = ${{ inputs.duration || 12 }}
+ antithesis.source = ${{ inputs.etcd_ref || 'main' }}
diff --git a/.github/workflows/antithesis-verify.yml b/.github/workflows/antithesis-verify.yml
new file mode 100644
index 000000000000..1341935cdf3e
--- /dev/null
+++ b/.github/workflows/antithesis-verify.yml
@@ -0,0 +1,72 @@
+---
+name: Verify Antithesis Docker Compose Pipeline
+
+permissions:
+ contents: read
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - 'tests/antithesis/**'
+ - '.github/workflows/antithesis-verify.yml'
+ pull_request:
+ paths:
+ - 'tests/antithesis/**'
+ - '.github/workflows/antithesis-verify.yml'
+
+jobs:
+ test-docker-compose:
+ strategy:
+ matrix:
+ node-count: [1, 3]
+ name: Test ${{ matrix.node-count }}-node cluster
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - name: Build etcd-server and etcd-client images
+ run: |
+ make -C tests/antithesis antithesis-build-etcd-image
+ make -C tests/antithesis antithesis-build-client-docker-image
+
+ - name: Run docker-compose up
+ working-directory: ./tests/antithesis
+ run: |
+ make antithesis-docker-compose-up CFG_NODE_COUNT=${{ matrix.node-count }} &
+
+ - name: Check for healthy cluster
+ working-directory: ./tests/antithesis
+ run: |
+ timeout=120
+ interval=10
+ end_time=$(( $(date +%s) + timeout ))
+
+ while [ $(date +%s) -lt $end_time ]; do
+ # The client container might not be running yet, so ignore errors from docker compose logs
+ if docker compose -f config/docker-compose-${{ matrix.node-count }}-node.yml logs client 2>/dev/null | grep -q "Client \[entrypoint\]: cluster is healthy!"; then
+ echo "Cluster is healthy!"
+ exit 0
+ fi
+ echo "Waiting for cluster to become healthy..."
+ sleep $interval
+ done
+
+ echo "Cluster did not become healthy in ${timeout} seconds."
+ docker compose -f config/docker-compose-${{ matrix.node-count }}-node.yml logs
+ exit 1
+
+ - name: Run traffic
+ working-directory: ./tests/antithesis
+ run: make antithesis-run-container-traffic CFG_NODE_COUNT=${{ matrix.node-count }}
+
+ - name: Run validation
+ working-directory: ./tests/antithesis
+ run: make antithesis-run-container-validation CFG_NODE_COUNT=${{ matrix.node-count }}
+
+ - name: Clean up
+ if: always()
+ working-directory: ./tests/antithesis
+ run: make antithesis-clean CFG_NODE_COUNT=${{ matrix.node-count }}
diff --git a/.github/workflows/antithesis.debugger.yml b/.github/workflows/antithesis.debugger.yml
new file mode 100644
index 000000000000..88fc25506d6a
--- /dev/null
+++ b/.github/workflows/antithesis.debugger.yml
@@ -0,0 +1,44 @@
+---
+name: Trigger Antithesis debugger
+
+on:
+ workflow_dispatch:
+ inputs:
+ session_id:
+ description: "The session_id of a test. Found at the bottom of a report."
+ required: true
+ type: string
+ input_hash:
+ description: "The input hash of a moment."
+ required: true
+ type: string
+ vtime:
+ description: "The vtime of a moment."
+ required: true
+ type: string
+ email:
+ description: 'Email notification recipient(s) (separate with ;)'
+ required: false
+ type: string
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ trigger-debugger:
+ runs-on: ubuntu-latest
+ environment: Antithesis
+ steps:
+ - name: Trigger Antithesis Debugger
+ uses: antithesishq/antithesis-trigger-action@72e1bc49d6a5f66c9b892e2b311844e00d342554 # v0.11
+ with:
+ notebook_name: debugging
+ tenant: linuxfoundation
+ username: ${{ secrets.ANTITHESIS_WEBHOOK_USERNAME }}
+ password: ${{ secrets.ANTITHESIS_WEBHOOK_PASSWORD }}
+ github_token: ${{ secrets.GH_PAT }}
+ email_recipients: ${{ inputs.email || 'siarkowicz@google.com' }}
+ additional_parameters: |-
+ antithesis.debugging.session_id = ${{ inputs.session_id }}
+ antithesis.debugging.input_hash = ${{ inputs.input_hash }}
+ antithesis.debugging.vtime = ${{ inputs.vtime }}
diff --git a/.github/workflows/bump-devcontainer-version.yml b/.github/workflows/bump-devcontainer-version.yml
new file mode 100644
index 000000000000..8dd32ecbe671
--- /dev/null
+++ b/.github/workflows/bump-devcontainer-version.yml
@@ -0,0 +1,47 @@
+---
+name: Bump devcontainer version
+on: pull_request
+permissions:
+ contents: write
+ pull-requests: read
+ workflows: write
+jobs:
+ dependabot-metadata:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'etcd-io/etcd'
+ outputs:
+ directory: ${{steps.metadata.outputs.directory}}
+ new-version: ${{steps.metadata.outputs.new-version}}
+ steps:
+ - name: Dependabot metadata
+ id: metadata
+ uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
+ with:
+ github-token: "${{secrets.GITHUB_TOKEN}}"
+ devcontainer-update:
+ runs-on: ubuntu-latest
+ needs: dependabot-metadata
+ if: needs.dependabot-metadata.outputs.directory == '/tools/container-images/devcontainer'
+ steps:
+ - name: Checkout the code
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{github.event.pull_request.head.ref}}
+ repository: ${{github.event.pull_request.head.repo.full_name}}
+ fetch-depth: 0
+ - name: Update devcontainer references
+ run: |
+ sed -i -E "s|(image: ).*|\1$(awk 'match($1, /FROM/){print $2}' tools/container-images/devcontainer/Dockerfile)|" .github/workflows/devcontainer-test.yml
+ sed -i -E "s|(mcr\.microsoft\.com/devcontainers/go:)[^"'"'"]+|\1${{needs.dependabot-metadata.outputs.new-version}}|" .devcontainer/devcontainer.json
+
+ git config user.name 'github-actions[bot]'
+ git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
+
+ git add .devcontainer/devcontainer.json .github/workflows/devcontainer-test.yml
+ git diff --cached --quiet && exit 0
+
+ git commit --signoff -m "build(deps): bump devcontainer version references
+
+ * Bumps .devcontainer/devcontainer.json to ${{needs.dependabot-metadata.outputs.new-version}}.
+ * Bumps .github/workflows/devcontainer-test.yml to ${{needs.dependabot-metadata.outputs.new-version}}."
+ git push origin "HEAD:refs/heads/${{github.event.pull_request.head.ref}}"
diff --git a/.github/workflows/cherrypick-bot-ok-to-test.yaml b/.github/workflows/cherrypick-bot-ok-to-test.yaml
new file mode 100644
index 000000000000..64fcb1942d11
--- /dev/null
+++ b/.github/workflows/cherrypick-bot-ok-to-test.yaml
@@ -0,0 +1,46 @@
+---
+name: Auto-label ok-to-test (cherrypick bot)
+permissions: read-all
+
+on:
+ pull_request_target:
+ types:
+ - labeled
+ branches:
+ - release-3.6
+ - release-3.5
+ - release-3.4
+
+jobs:
+ add-label:
+ name: Add label
+ # 90416843 = k8s-infra-cherrypick-robot account ID.
+ if: |
+ github.event.pull_request.user.id == 90416843 &&
+ github.event.label.name == 'needs-ok-to-test'
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Update PR
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ debug: ${{ secrets.ACTIONS_RUNNER_DEBUG == 'true' }}
+ script: |
+ try {
+ await github.rest.issues.removeLabel({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: 'needs-ok-to-test'
+ });
+ } catch (e) {
+ if (e.status !== 404) throw e;
+ }
+ await github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: ['ok-to-test']
+ });
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index dddd2746729a..a8468a3527d3 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -37,10 +37,10 @@ jobs:
language: ['go']
steps:
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
+ uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
with:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
@@ -50,6 +50,6 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
+ uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
+ uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
diff --git a/.github/workflows/devcontainer-test.yml b/.github/workflows/devcontainer-test.yml
new file mode 100644
index 000000000000..1f1a014a5c25
--- /dev/null
+++ b/.github/workflows/devcontainer-test.yml
@@ -0,0 +1,18 @@
+---
+name: Test devcontainer
+on:
+ pull_request_target:
+ paths:
+ - '.devcontainer/devcontainer.json'
+permissions: read-all
+jobs:
+ test-devcontainer:
+ runs-on: ubuntu-latest
+ container:
+ image: mcr.microsoft.com/devcontainers/go:1.26-bookworm
+ steps:
+ - name: Build project using defined devcontainer
+ run: |
+ git clone --depth=1 --branch=${GITHUB_REF_NAME} https://github.com/${GITHUB_REPOSITORY} etcd
+ cd etcd
+ make build
diff --git a/.github/workflows/gh-workflow-approve.yaml b/.github/workflows/gh-workflow-approve.yaml
index 1f988637677c..6a4c30691edd 100644
--- a/.github/workflows/gh-workflow-approve.yaml
+++ b/.github/workflows/gh-workflow-approve.yaml
@@ -9,6 +9,7 @@ on:
- synchronize
branches:
- main
+ - release-3.6
- release-3.5
- release-3.4
@@ -21,7 +22,7 @@ jobs:
actions: write
steps:
- name: Update PR
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/measure-testgrid-flakiness.yaml b/.github/workflows/measure-testgrid-flakiness.yaml
index 67bdd68d39f0..13bdf0ff6f70 100644
--- a/.github/workflows/measure-testgrid-flakiness.yaml
+++ b/.github/workflows/measure-testgrid-flakiness.yaml
@@ -12,10 +12,10 @@ jobs:
name: Measure TestGrid Flakiness
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: goversion
run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
- - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
+ - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version: ${{ steps.goversion.outputs.goversion }}
- env:
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 6b74fa8d59c2..d6b8d88c686f 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -23,12 +23,12 @@ jobs:
steps:
- name: "Checkout code"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
- uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
+ uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
@@ -42,7 +42,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
- uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif
@@ -50,6 +50,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
+ uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
with:
sarif_file: results.sarif
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
new file mode 100644
index 000000000000..ebfbcf044c78
--- /dev/null
+++ b/.github/workflows/stale.yaml
@@ -0,0 +1,30 @@
+---
+name: Mark and close stale issues and PRs
+
+on:
+ schedule:
+ - cron: '0 0 * * *'
+
+permissions:
+ issues: write
+ pull-requests: write
+
+jobs:
+ stale:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 #v10.3.0
+ with:
+ days-before-stale: 90
+ days-before-close: 21
+ stale-issue-label: 'stale'
+ stale-pr-label: 'stale'
+ exempt-issue-labels: 'stage/tracked,help wanted'
+ exempt-pr-labels: 'stage/tracked,help wanted'
+ exempt-milestones: ''
+ exempt-assignees: ''
+ stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.'
+ stale-pr-message: 'This pull request has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions.'
+ close-issue-message: ''
+ close-pr-message: ''
+ operations-per-run: 30
diff --git a/.go-version b/.go-version
index e6a6e7cd3e99..8fe00a57fe1d 100644
--- a/.go-version
+++ b/.go-version
@@ -1 +1 @@
-1.25.8
+1.26.5
diff --git a/CHANGELOG/CHANGELOG-3.7.md b/CHANGELOG/CHANGELOG-3.7.md
new file mode 100644
index 000000000000..e552f2813c66
--- /dev/null
+++ b/CHANGELOG/CHANGELOG-3.7.md
@@ -0,0 +1,31 @@
+
+
+Previous change logs can be found at [CHANGELOG-3.6](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.6.md).
+
+
+
+## v3.7.0 (TBD)
+
+### Deprecations
+
+- Deprecated [UsageFunc in pkg/cobrautl](https://github.com/etcd-io/etcd/pull/18356).
+
+### etcdctl
+
+- [Organize etcdctl commands](https://github.com/etcd-io/etcd/pull/20162) to make them more concise and easier to understand.
+- [Hide the global flags](https://github.com/etcd-io/etcd/pull/20493) to make the output of `etcdctl --help` looks cleaner and is consistent with kubectl.
+
+### etcdutl
+
+- [Add a timeout flag to all etcdutl commands](https://github.com/etcd-io/etcd/pull/20708) when waiting to acquire a file lock on the database file.
+
+### Metrics, Monitoring
+
+See [List of metrics](https://etcd.io/docs/latest/metrics/) for all metrics per release.
+
+- Add [`etcd_server_request_duration_seconds`](https://github.com/etcd-io/etcd/pull/21038).
+- Add [the following metrics related to watch send loop](https://github.com/etcd-io/etcd/pull/21030),
+ - etcd_debugging_server_watch_send_loop_watch_stream_duration_seconds
+ - etcd_debugging_server_watch_send_loop_watch_stream_duration_per_event_seconds
+ - etcd_debugging_server_watch_send_loop_control_stream_duration_seconds
+ - etcd_debugging_server_watch_send_loop_progress_duration_seconds
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 08d1807e9879..73c04eda604b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -55,7 +55,7 @@ For more, because etcd uses Kubernetes' prow infrastructure to run CI jobs, the
| periodics e2e-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-periodics/ci-etcd-e2e-amd64) |
| presubmit build | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-build) |
| presubmit e2e-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-e2e-amd64) |
-| presubmit unit-test | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-unit-test) |
+| presubmit unit-test-amd64 | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-unit-test-amd64) |
| presubmit verify | [](https://testgrid.k8s.io/q/summary/sig-etcd-presubmits/pull-etcd-verify) |
| postsubmit build | [](https://testgrid.k8s.io/q/summary/sig-etcd-postsubmits/post-etcd-build) |
diff --git a/Dockerfile b/Dockerfile
index 8d9db55f6fb6..e5ff38389b68 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
ARG ARCH=amd64
-FROM --platform=linux/${ARCH} gcr.io/distroless/static-debian12@sha256:20bc6c0bc4d625a22a8fde3e55f6515709b32055ef8fb9cfbddaa06d1760f838
+FROM --platform=linux/${ARCH} gcr.io/distroless/static-debian12@sha256:9c346e4be81b5ca7ff31a0d89eaeade58b0f95cfd3baed1f36083ddb47ca3160
ADD etcd /usr/local/bin/
ADD etcdctl /usr/local/bin/
diff --git a/Documentation/contributor-guide/bump_etcd_version_k8s.md b/Documentation/contributor-guide/bump_etcd_version_k8s.md
new file mode 100644
index 000000000000..7b157ef6ed4a
--- /dev/null
+++ b/Documentation/contributor-guide/bump_etcd_version_k8s.md
@@ -0,0 +1,191 @@
+# Bump etcd Version in Kubernetes
+
+This guide will walk through the update of etcd in Kubernetes to a new version (`kubernetes/kubernetes` repository).
+
+> Currently we bump etcd v3.5.x for K8s release-1.33 and lower versions, and we bump etcd v3.6.x for K8s release-1.34 and higher versions.
+
+You can use this [issue](https://github.com/kubernetes/kubernetes/issues/131101) as a reference when updating the etcd version in Kubernetes.
+
+Bumping the etcd version in Kubernetes consists of two steps.
+
+* Bump etcd client SDK
+* Bump etcd image
+
+> The commented lines in this document signifies the line to be changed
+
+## Bump etcd client SDK
+
+> Reference: [link](https://github.com/kubernetes/kubernetes/pull/131103)
+
+You can refer to the guide [here](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/vendor.md) under the **Adding or updating a dependency** section.
+
+1. Get all the etcd modules used in Kubernetes.
+
+ ```bash
+ $ grep 'go.etcd.io/etcd/' go.mod | awk '{print $1}'
+ go.etcd.io/etcd/api/v3
+ go.etcd.io/etcd/client/pkg/v3
+ go.etcd.io/etcd/client/v3
+ go.etcd.io/etcd/client/v2
+ go.etcd.io/etcd/pkg/v3
+ go.etcd.io/etcd/raft/v3
+ go.etcd.io/etcd/server/v3
+ ```
+
+2. For each module, in the root directory of the `kubernetes/kubernetes` repository, fetch the new version in `go.mod` using the following command (using `client/v3` as an example):
+
+ ```bash
+ hack/pin-dependency.sh go.etcd.io/etcd/client/v3 NEW_VERSION
+ ```
+
+3. Rebuild the `vendor` directory and update the `go.mod` files for all staging repositories using the command below. This automatically updates the licenses.
+
+ ```bash
+ hack/update-vendor.sh
+ ```
+
+4. Check if the new dependency requires newer versions of existing dependencies we have pinned. You can check this by:
+
+ * Running `hack/lint-dependencies.sh` against your branch and against `master` and comparing the results.
+ * Checking if any new `replace` directives were added to `go.mod` files of components inside the staging directory.
+
+## Bump etcd image
+
+### Build etcd image
+
+> Reference: [link 1](https://github.com/kubernetes/kubernetes/pull/131105) [link 2](https://github.com/kubernetes/kubernetes/pull/131126)
+
+1. In `build/dependencies.yaml`, update the `version` of `etcd-image` to the new version. Update `golang: etcd release version` if necessary.
+
+ ```yaml
+ - name: "etcd-image"
+ # version: 3.5.17
+ version: 3.5.21
+ refPaths:
+ - path: cluster/images/etcd/Makefile
+ match: BUNDLED_ETCD_VERSIONS\?|
+ ---
+ - name: "golang: etcd release version"
+ # version: 1.22.9
+ version: 1.23.7 # https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-3.6.md
+ ```
+
+2. In `cluster/images/etcd/Makefile`, include the new version in `BUNDLED_ETCD_VERSIONS` and update the `LATEST_ETCD_VERSION` as well (the image tag will be generated from the `LATEST_ETCD_VERSION`). Update `GOLANG_VERSION` according to the version used to compile that release version (`"golang: etcd release version"` in step 1).
+
+ ```Makefile
+ # BUNDLED_ETCD_VERSIONS?=3.4.18 3.5.17
+ BUNDLED_ETCD_VERSIONS?=3.4.18 3.5.21
+
+ # LATEST_ETCD_VERSION?=3.5.17
+ LATEST_ETCD_VERSION?=3.5.21
+
+ # GOLANG_VERSION := 1.22.9
+ GOLANG_VERSION := 1.23.7
+ ```
+
+3. In `cluster/images/etcd/migrate/options.go`, include the new version in the `supportedEtcdVersions` slice.
+
+ ```go
+ var (
+ // supportedEtcdVersions = []string{"3.4.18", "3.5.17"}
+ supportedEtcdVersions = []string{"3.4.18", "3.5.21"}
+ )
+ ```
+
+### Publish etcd image
+
+> Reference: [link](https://github.com/kubernetes/k8s.io/pull/7957)
+
+1. When the previous step is merged, a post-commit job will run to build the image. You can find the newly built image in the [registry](https://gcr.io/k8s-staging-etcd/etcd).
+
+2. Locate the newly built image and copy its SHA256 digest.
+
+3. Inside the `kubernetes/k8s.io` repository, in `registry.k8s.io/images/k8s-staging-etcd/images.yaml`, create a new entry for the desired version and copy the SHA256 digest.
+
+ ```yaml
+ "sha256:b4a9e4a7e1cf08844c7c4db6a19cab380fbf0aad702b8c01e578e9543671b9f9": ["3.5.17-0"]
+ # ADD:
+ "sha256:d58c035df557080a27387d687092e3fc2b64c6d0e3162dc51453a115f847d121": ["3.5.21-0"]
+ ```
+
+### Update to use the new etcd image
+
+> Reference: [link](https://github.com/kubernetes/kubernetes/pull/131144)
+
+1. In `build/dependencies.yaml`, change the `version` of `etcd` to the new version.
+
+ ```yaml
+ # etcd
+ - name: "etcd"
+ # version: 3.5.17
+ version: 3.5.21
+ refPaths:
+ - path: cluster/gce/manifests/etcd.manifest
+ match: etcd_docker_tag|etcd_version
+ ```
+
+2. In `cluster/gce/manifests/etcd.manifest`, change the image tag to the new image tag and `TARGET_VERSION` to the new version.
+
+ ```manifest
+ // "image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.5.17-0') }}",
+
+ "image": "{{ pillar.get('etcd_docker_repository', 'registry.k8s.io/etcd') }}:{{ pillar.get('etcd_docker_tag', '3.5.21-0') }}",
+
+ ---
+
+ { "name": "TARGET_VERSION",
+ // "value": "{{ pillar.get('etcd_version', '3.5.17') }}"
+ "value": "{{ pillar.get('etcd_version', '3.5.21') }}"
+ },
+ ```
+
+3. In `cluster/gce/upgrade-aliases.sh`, update the exports for `ETCD_IMAGE` to the new image tag and `ETCD_VERSION` to the new version.
+
+ ```sh
+ # export ETCD_IMAGE=3.5.17-0
+ export ETCD_IMAGE=3.5.21-0
+ # export ETCD_VERSION=3.5.17
+ export ETCD_VERSION=3.5.21
+ ```
+
+4. In `cmd/kubeadm/app/constants/constants.go`, change the `DefaultEtcdVersion` to the new version. In the same file, update `SupportedEtcdVersion` accordingly.
+
+ ```go
+ // DefaultEtcdVersion = "3.5.17-0"
+ DefaultEtcdVersion = "3.5.21-0"
+
+ ---
+
+ SupportedEtcdVersion = map[uint8]string{
+ // 30: "3.5.17-0",
+ // 31: "3.5.17-0",
+ // 32: "3.5.17-0",
+ // 33: "3.5.17-0",
+ 30: "3.5.21-0",
+ 31: "3.5.21-0",
+ 32: "3.5.21-0",
+ 33: "3.5.21-0",
+ }
+ ```
+
+5. In `hack/lib/etcd.sh`, update the `ETCD_VERSION`.
+
+ ```sh
+ # ETCD_VERSION=${ETCD_VERSION:-3.5.17}
+ ETCD_VERSION=${ETCD_VERSION:-3.5.21}
+ ```
+
+6. In `staging/src/k8s.io/sample-apiserver/artifacts/example/deployment.yaml`, update the etcd image used.
+
+ ```yaml
+ - name: etcd
+ # image: gcr.io/etcd-development/etcd:v3.5.17
+ image: gcr.io/etcd-development/etcd:v3.5.21
+ ```
+
+7. In `test/utils/image/manifest.go`, update the etcd image tag.
+
+ ```go
+ // configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.5.17-0"}
+ configs[Etcd] = Config{list.GcEtcdRegistry, "etcd", "3.5.21-0"}
+ ```
diff --git a/Documentation/contributor-guide/cherry-pick.md b/Documentation/contributor-guide/cherry-pick.md
new file mode 100644
index 000000000000..ef58a86b9402
--- /dev/null
+++ b/Documentation/contributor-guide/cherry-pick.md
@@ -0,0 +1,52 @@
+# Cherry-picking changes
+
+Cherry-picking applies changes from a PR to a release branch.
+
+## Recommended workflow (automated)
+
+Cherry-picks are primarily handled using the **k8s-infra-cherrypick-robot**.
+
+For example, to cherry-pick a PR onto `release-3.6`, comment on the PR with:
+
+```bash
+/cherry-pick release-3.6
+```
+
+The bot will create a cherry-pick PR against the target release branch.
+
+---
+
+## Fallback: manual cherry-pick script
+
+This script can be used for manual cherry-picks in cases where the automated cherry-pick does not work.
+
+## Setup
+
+Set the `UPSTREAM_REMOTE` and `FORK_REMOTE` environment variables.
+`UPSTREAM_REMOTE` should be set to git remote name of `github.com/etcd-io/etcd`,
+and `FORK_REMOTE` should be set to the git remote name of the forked etcd
+repo (`github.com/${github-username}/etcd`). Use `git remote -v` to
+look up the git remote names. If etcd has not been forked, create
+one on github.com and register it locally with `git remote add ...`.
+
+```bash
+export UPSTREAM_REMOTE=upstream
+export FORK_REMOTE=origin
+export GITHUB_USER=${github-username}
+```
+
+Next, install hub from
+
+## Usage
+
+To cherry pick PR 12345 onto release-3.6 and propose is as a PR, run:
+
+```sh
+./scripts/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.6 12345
+```
+
+To cherry pick 12345 then 56789 and propose them togther as a single PR, run:
+
+```bash
+./scripts/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.6 12345 56789
+```
diff --git a/Documentation/contributor-guide/community-membership.md b/Documentation/contributor-guide/community-membership.md
index 15d5615a0ca3..a61832664f78 100644
--- a/Documentation/contributor-guide/community-membership.md
+++ b/Documentation/contributor-guide/community-membership.md
@@ -1,6 +1,6 @@
# Community membership
-This doc outlines the various responsibilities of contributor roles in etcd.
+This doc outlines the various responsibilities of contributor roles in etcd.
| Role | Responsibilities | Requirements | Defined by |
|------------|----------------------------------------------|---------------------------------------------------------------|-------------------------------|
@@ -25,38 +25,38 @@ below.
## Member
Members are continuously active contributors to the community. They can have
-issues and PRs assigned to them. Members are expected to remain active
+issues and PRs assigned to them. Members are expected to remain active
contributors to the community.
**Defined by:** Member of the etcd GitHub organization.
-### Requirements
+### Member requirements
- Enabled [two-factor authentication] on their GitHub account
- Have made multiple contributions to the project or community. Contribution may include, but is not limited to:
- - Authoring or reviewing PRs on GitHub. At least one PR must be **merged**.
- - Filing or commenting on issues on GitHub
- - Contributing to community discussions (e.g. meetings, Slack, email discussion
+ - Authoring or reviewing PRs on GitHub. At least one PR must be **merged**.
+ - Filing or commenting on issues on GitHub
+ - Contributing to community discussions (e.g. meetings, Slack, email discussion
forums, Stack Overflow)
- Subscribed to [etcd-dev@googlegroups.com](https://groups.google.com/g/etcd-dev)
- Have read the [contributor guide]
- Sponsored by two active maintainers or reviewers.
- - Sponsors must be from multiple member companies to demonstrate integration across the community.
- - With no objections from other maintainers
+ - Sponsors must be from multiple member companies to demonstrate integration across the community.
+ - With no objections from other maintainers
- Open a [membership nomination] issue against the `kubernetes/org` repo
- - Ensure your sponsors are @mentioned on the issue
- - Make sure that the list of contributions included is representative of your work on the project.
+ - Ensure your sponsors are @mentioned on the issue
+ - Make sure that the list of contributions included is representative of your work on the project.
- Members can be removed by a supermajority of the maintainers or can resign by notifying
the maintainers.
-### Responsibilities and privileges
+### Member responsibilities and privileges
- Responsive to issues and PRs assigned to them
- Granted "triage access" to etcd project
- Active owner of code they have contributed (unless ownership is explicitly transferred)
- - Code is well-tested
- - Tests consistently pass
- - Addresses bugs or issues discovered after code is accepted
+ - Code is well-tested
+ - Tests consistently pass
+ - Addresses bugs or issues discovered after code is accepted
**Note:** Members who frequently contribute code are expected to proactively
perform code reviews and work towards becoming a *reviewer*.
@@ -64,31 +64,31 @@ perform code reviews and work towards becoming a *reviewer*.
## Reviewers
Reviewers are contributors who have demonstrated greater skill in
-reviewing the code from other contributors. They are knowledgeable about both
+reviewing the code from other contributors. They are knowledgeable about both
the codebase and software engineering principles. Their LGTM counts towards
merging a code change into the project. A reviewer is generally on the ladder towards
-maintainership.
+maintainership.
**Defined by:** *reviewers* entry in the [OWNERS] file.
-### Requirements
+### Reviewer requirements
- member for at least 3 months.
- Primary reviewer for at least 5 PRs to the codebase.
- Reviewed or contributed at least 20 substantial PRs to the codebase.
- Knowledgeable about the codebase.
- Sponsored by two active maintainers.
- - Sponsors must be from multiple member companies to demonstrate integration across the community.
- - With no objections from other maintainers
+ - Sponsors must be from multiple member companies to demonstrate integration across the community.
+ - With no objections from other maintainers
- Reviewers can be removed by a supermajority of the maintainers or can resign by notifying
the maintainers.
-### Responsibilities and privileges
+### Reviewer responsibilities and privileges
- Code reviewer status may be a precondition to accepting large code contributions
- Responsible for project quality control via code reviews
- - Focus on code quality and correctness, including testing and factoring
- - May also review for more holistic issues, but not a requirement
+ - Focus on code quality and correctness, including testing and factoring
+ - May also review for more holistic issues, but not a requirement
- Expected to be responsive to review requests
- Assigned PRs to review related to area of expertise
- Assigned test bugs related to area of expertise
@@ -103,30 +103,32 @@ depend on to make decisions in the best interest of the project in a consistent
**Defined by:** *approvers* entry in the [OWNERS] file.
-### Requirements
+### Maintainer requirements
- Deep understanding of the technical goals and direction of the project
- Deep understanding of the technical domain of the project
- Sustained contributions to design and direction by doing all of:
- - Authoring and reviewing proposals
- - Initiating, contributing, and resolving discussions (emails, GitHub issues, meetings)
- - Identifying subtle or complex issues in the designs and implementation of PRs
+ - Authoring and reviewing proposals
+ - Initiating, contributing, and resolving discussions (emails, GitHub issues, meetings)
+ - Identifying subtle or complex issues in the designs and implementation of PRs
- Directly contributed to the project through implementation and/or review
- Sponsored by two active maintainers and elected by supermajority
- - Sponsors must be from multiple member companies to demonstrate integration across the community.
-- To become a maintainer send an email with your candidacy to etcd-maintainers-private@googlegroups.com
- - Ensure your sponsors are @mentioned in the email
- - Include a list of contributions representative of your work on the project.
- - Existing maintainers vote will privately and respond to the email with either acceptance or feedback for suggested improvement.
+ - Sponsors must be from multiple member companies to demonstrate integration across the community.
+- To become a maintainer send an email with your candidacy to
+ - Ensure your sponsors are @mentioned in the email
+ - Include a list of contributions representative of your work on the project.
+ - Existing maintainers vote will privately and respond to the email with either acceptance or feedback for suggested improvement.
- With your membership approved you are expected to:
- Open a PR and add an entry to the [OWNERS] file
- - Subscribe to etcd-maintainers@googlegroups.com and etcd-maintainers-private@googlegroups.com
- - Request to join [etcd-maintainer teams of etcd organization of GitHub](https://github.com/orgs/etcd-io/teams/maintainers-etcd)
+ - Request to be added to the and mailing lists
+ - Request to join [etcd-maintainer teams of the etcd-io organization in GitHub](https://github.com/orgs/etcd-io/teams/maintainers-etcd)
- Request to join the private slack channel for etcd maintainers on [kubernetes slack](http://slack.kubernetes.io/)
- - Request access to etcd-development GCP project where we publish releases
+ - Request access to `etcd-development` GCP project where we publish releases
- Request access to passwords shared between maintainers
+ - Request cncf service desk access by emailing
+ - Raise cncf service desk ticket to be addded to [cncf-etcd-maintainers mailing list](https://lists.cncf.io/g/cncf-etcd-maintainers/directory)
-### Responsibilities and privileges
+### Maintainer responsibilities and privileges
- Make and approve technical design decisions
- Set technical direction and priorities
@@ -134,8 +136,8 @@ depend on to make decisions in the best interest of the project in a consistent
- Mentor and guide reviewers, and contributors to the project.
- Participate when called upon in the [security disclosure and release process]
- Ensure the continued health of the project
- - Adequate test coverage to confidently release
- - Tests are passing reliably (i.e. not flaky) and are fixed when they fail
+ - Adequate test coverage to confidently release
+ - Tests are passing reliably (i.e. not flaky) and are fixed when they fail
- Ensure a healthy process for discussion and decision-making is in place.
- Work with other maintainers to maintain the project's overall health and success holistically
@@ -144,8 +146,7 @@ depend on to make decisions in the best interest of the project in a consistent
Life priorities, interests, and passions can change. Maintainers can retire and
move to [emeritus maintainers]. If a maintainer needs to step down, they should
inform other maintainers and, if possible, help find someone to pick up the related
-work. At the very least, ensure the related work can be continued. Afterward,
-they can remove themselves from the list of existing maintainers.
+work. At the very least, ensure the related work can be continued.
If a maintainer has not been performing their duties for 12 months,
they can be removed by other maintainers. In that case, the inactive maintainer will
@@ -155,6 +156,14 @@ so by renewing their contributions. Active maintainers should welcome such a mov
Retiring other maintainers or regaining the status should require the approval
of at least two active maintainers.
+Retiring maintainers must:
+
+- Open a PR and move to emeritus approvers in the [OWNERS] file
+- Open a PR to be removed from the [etcd-maintainer teams of the etcd-io organization in GitHub](https://github.com/orgs/etcd-io/teams/maintainers-etcd)
+- Remove their access to `etcd-development` GCP project where we publish releases
+- Raise cncf service desk ticket to be removed as a [cncf-etcd-maintainers mailing list](https://lists.cncf.io/g/cncf-etcd-maintainers/directory) admin
+- Request to be removed as a member of the [etcd-maintainers](https://groups.google.com/g/etcd-maintainers) and [etcd-maintainers-private](https://groups.google.com/g/etcd-maintainers-private) Google groups
+
## Acknowledgements
Contributor roles and responsibilities were written based on [Kubernetes community membership]
@@ -166,4 +175,3 @@ Contributor roles and responsibilities were written based on [Kubernetes communi
[emeritus maintainers]: /README.md#etcd-emeritus-maintainers
[security disclosure and release process]: /security/README.md
[two-factor authentication]: https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication
-
diff --git a/Documentation/contributor-guide/dependency_management.md b/Documentation/contributor-guide/dependency_management.md
index 8b022b424f8f..57c4e6d76d4e 100644
--- a/Documentation/contributor-guide/dependency_management.md
+++ b/Documentation/contributor-guide/dependency_management.md
@@ -6,6 +6,7 @@
- [Dependencies used in workflows](#dependencies-used-in-workflows)
- [Bumping order](#bumping-order)
- [Steps to bump a dependency](#steps-to-bump-a-dependency)
+ - [Alternative: Using the update_dep.sh script](#alternative-using-the-update_depsh-script)
- [Indirect dependencies](#indirect-dependencies)
- [Known incompatible dependency updates](#known-incompatible-dependency-updates)
- [arduino/setup-protoc](#arduinosetup-protoc)
@@ -57,7 +58,7 @@ cd ${ETCD_ROOT_DIR}/etcdctl
go get github.com/spf13/cobra@v1.7.0
go mod tidy
cd ..
-./scripts/fix.sh
+make fix # This will update the bill of materials, Go modules and workspace, etc.
```
Execute the same steps for all other modules. When you finish bumping the dependency for all modules, then commit the change,
@@ -72,7 +73,31 @@ Please close the related PRs which were automatically opened by dependabot.
When you bump multiple dependencies in one PR, it's recommended to create a separate commit for each dependency. But it isn't a must; for example,
you can get all dependencies bumping for the module `go.etcd.io/etcd/tools/v3` included in one commit.
-#### Troubleshooting
+#### Alternative: Using the update_dep.sh script
+
+> Note: Please use bash shell version 5.x or higher.
+
+As an alternative to the manual steps above, you can use the `update_dep.sh` script to automate the dependency bump process across all modules:
+
+```bash
+# Update to a specific version
+./scripts/update_dep.sh github.com/spf13/cobra v1.7.0
+
+# Update to the latest version
+./scripts/update_dep.sh github.com/spf13/cobra
+```
+
+The script will:
+
+1. Display the current version of the dependency across all go.mod files
+2. Warn and prompt for confirmation if the dependency is purely indirect
+3. Update the dependency in all modules that depend on it
+4. Run `make fix verify-dep` to ensure consistency across all modules
+5. Display the updated versions for verification
+
+This script handles the correct bumping order automatically and ensures version consistency across all modules.
+
+#### Troubleshooting
In an event of bumping the version of protoc, protoc plugins or grpc-gateway, it might change `*.proto` file which can result in the following error:
@@ -120,12 +145,18 @@ and everyone is welcome to participate; you just need to register your name in t
Usually, we don't proactively bump dependencies for stable releases unless there are any CVEs or bugs that affect etcd.
-If we have to do it, then follow the same guidance above. Note that there is no `./scripts/fix.sh` in release-3.4, so no need to
+If we have to do it, then follow the same guidance above. Note that there is no `./scripts/fix.sh`/`make fix` in release-3.4, so no need to
execute it for 3.4.
## Golang versions
-The etcd project aims to maintain a development branch that is on the latest [Go version](https://go.dev/dl), ideally, this will align with the Go version in use for Kubernetes project development. For an example of how to update etcd to a new minor release of Go refer to issue and the linked pull requests.
+For all libraries that exist as independent subprojects (e.g., bbolt, raft, gofail), we should always stick
+to the oldest supported Go minor version for all branches, including main. It's up to the users of these
+libraries to choose which [Go version](https://go.dev/dl) they want to use in their own projects.
+
+For other subprojects that produce binaries or images (e.g. etcd, etcd-operator, auger), the main
+branches should use the latest Go minor version for development, while stable releases should use the
+latest patch of the previous supported Go minor version to ensure stability.
Suggested steps for performing a minor version upgrade for the etcd development branch:
@@ -147,11 +178,11 @@ References:
[bbolt](https://github.com/etcd-io/bbolt) and [raft](https://github.com/etcd-io/raft) are two core dependencies of etcd.
-Both etcd 3.4.x and 3.5.x depend on bbolt 1.3.x, and etcd 3.6.x (`main` branch) depends on bbolt 1.4.x.
+Both etcd 3.4.x and 3.5.x depend on bbolt 1.3.x, and etcd 3.6.x depends on bbolt 1.4.x.
raft is included in the etcd repository for release-3.4 and release-3.5 branches, so etcd 3.4.x and 3.5.x do not depend on any
-external raft module. We moved raft into [a separate repository](https://github.com/etcd-io/raft) starting from 3.6 (`main` branch), and the first raft
-release will be v3.6.0, so etcd 3.6.x will depend on raft 3.6.x.
+external raft module. We moved raft into [a separate repository](https://github.com/etcd-io/raft) starting from 3.6, and the first raft
+release is v3.6.0, so etcd 3.6.0 depends on raft v3.6.0.
Please see the table below:
@@ -160,3 +191,4 @@ Please see the table below:
| 3.4.x | v1.3.x | N/A |
| 3.5.x | v1.3.x | N/A |
| 3.6.x | v1.4.x | v3.6.x |
+| 3.7.x | v1.5.x | v3.7.x |
diff --git a/Documentation/contributor-guide/exit_codes.md b/Documentation/contributor-guide/exit_codes.md
new file mode 100644
index 000000000000..db8cbe65219d
--- /dev/null
+++ b/Documentation/contributor-guide/exit_codes.md
@@ -0,0 +1,54 @@
+# Exit Codes Reference
+
+This document provides a reference of exit codes returned by the etcd server.
+
+etcd server explicitly uses three exit codes: **0** (success), **1** (general errors), and **2** (argument errors). When terminated by signals (SIGTERM/SIGINT) on Linux/Unix systems, the exit code depends on the process type: PID 1 processes exit with code 0, while non-PID 1 processes re-raise the signal, resulting in exit codes 143 (SIGTERM) or 130 (SIGINT).
+
+## Exit Code 0 - Success
+
+| Scenario | Code Reference |
+| -------- | -------------- |
+| Help flag (`--help`) | [`server/etcdmain/config.go#L131`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L131) |
+| Version flag (`--version`) | [`server/etcdmain/config.go#L144`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L144) |
+| Normal shutdown | [`server/etcdmain/etcd.go#L176`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L176) |
+| Graceful shutdown on signal (PID 1) | [`pkg/osutil/interrupt_unix.go#L74`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L74) |
+
+## Signal Termination (128 + signal number)
+
+**Note:** This behavior is specific to Linux platform. On other platforms, etcd typically returns exit code 0 if it exits without error, and 1 otherwise.
+
+For non-PID 1 processes on Linux/Unix, the signal handler re-raises the signal to the process itself ([`pkg/osutil/interrupt_unix.go#L77`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L77)), which results in the kernel setting the exit code to 128 + signal number.
+
+| Signal | Exit Code | Code Reference |
+| ------ | --------- | -------------- |
+| SIGINT (Ctrl-C) | 130 (128 + 2) | [`pkg/osutil/interrupt_unix.go#L53`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L53) |
+| SIGTERM | 143 (128 + 15) | [`pkg/osutil/interrupt_unix.go#L53`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/pkg/osutil/interrupt_unix.go#L53) |
+
+## Exit Code 1 - General Errors
+
+All server errors exit with code 1:
+
+| Scenario | Code Reference |
+| -------- | -------------- |
+| Failed to create logger | [`server/etcdmain/etcd.go#L60`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L60) |
+| Failed to verify flags | [`server/etcdmain/etcd.go#L69`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L69) |
+| Discovery token already used | [`server/etcdmain/etcd.go#L141`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L141) |
+| Initial cluster configuration error | [`server/etcdmain/etcd.go#L155`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L155) |
+| Discovery failed | [`server/etcdmain/etcd.go#L157`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L157) |
+| Listener failed | [`server/etcdmain/etcd.go#L172`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L172) |
+| Failed to list data directory | [`server/etcdmain/etcd.go#L201`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L201) |
+| Invalid datadir (member + proxy exist) | [`server/etcdmain/etcd.go#L221`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L221) |
+| Unsupported architecture | [`server/etcdmain/etcd.go#L252`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/etcd.go#L252) |
+| Generic fatal error | [`server/etcdmain/util.go#L34`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/util.go#L34) |
+| Invalid listen-peer-urls | [`server/embed/config.go#L821`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L821) |
+| Invalid listen-client-urls | [`server/embed/config.go#L830`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L830) |
+| Invalid listen-client-http-urls | [`server/embed/config.go#L839`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L839) |
+| Invalid initial-advertise-peer-urls | [`server/embed/config.go#L848`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L848) |
+| Invalid advertise-client-urls | [`server/embed/config.go#L857`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L857) |
+| Invalid listen-metrics-urls | [`server/embed/config.go#L866`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/embed/config.go#L866) |
+
+## Exit Code 2 - Argument Errors
+
+| Scenario | Code Reference |
+| -------- | -------------- |
+| Flag parsing error | [`server/etcdmain/config.go#L133`](https://github.com/etcd-io/etcd/blob/e0a72cf470756149f4f602bf89284038e6397549/server/etcdmain/config.go#L133) |
diff --git a/Documentation/contributor-guide/features.md b/Documentation/contributor-guide/features.md
index 5f98391077b7..ed2d55accd36 100644
--- a/Documentation/contributor-guide/features.md
+++ b/Documentation/contributor-guide/features.md
@@ -4,80 +4,84 @@ This document provides an overview of etcd features and general development guid
## Overview
-The etcd features fall into three stages, experimental, stable, and unsafe.
+The etcd features fall into three stages: Alpha, Beta, and GA.
-### Experimental
+### Alpha
-Any new feature is usually added as an experimental feature. An experimental feature is characterized as below:
+Any new feature is usually added as an Alpha feature. An Alpha feature is characterized as below:
- Might be buggy due to a lack of user testing. Enabling the feature may not work as expected.
-- Disabled by default when added initially.
+- Disabled by default.
- Support for such a feature may be dropped at any time without notice
- Feature-related issues may be given lower priorities.
- - It can be removed in the next minor or major release without following the feature deprecation policy unless it graduates to a stable future.
+ - It can be removed in the next minor or major release without following the feature deprecation policy unless it graduates to a more stable stage.
-### Stable
+### Beta
-A stable feature is characterized as below:
+A Beta feature is characterized as below:
- Supported as part of the supported releases of etcd.
-- May be enabled by default.
+- Enabled by default.
- Discontinuation of support must follow the feature deprecation policy.
-### Unsafe
+### GA
-Unsafe features are rare and listed under the `Unsafe feature:` section in the etcd usage documentation. By default, they are disabled. They should be used with caution following documentation. An unsafe feature can be removed in the next minor or major release without following the feature deprecation policy.
+A GA feature is characterized as below:
+- Supported as part of the supported releases of etcd.
+- Always enabled; you cannot disable it. The corresponding feature gate is no longer needed.
+- Discontinuation of support must follow the feature deprecation policy.
## Development Guidelines
### Adding a new feature
-Any new enhancements to the etcd are typically added as an experimental feature. The general development requirements are listed below. They can be somewhat flexible depending on the scope of the feature and review discussions and will evolve over time.
-- Open an issue
+Any new enhancements to the etcd are typically added as an Alpha feature.
+
+etcd follows the Kubernetes [KEP process](https://github.com/kubernetes/enhancements/blob/master/keps/sig-architecture/0000-kep-process/README.md) for new enhancements. The general development requirements are listed below. They can be somewhat flexible depending on the scope of the feature and review discussions and will evolve over time.
+- Open a [KEP](https://github.com/kubernetes/enhancements/issues) issue
- It must provide a clear need for the proposed feature.
- - It should list development work items as checkboxes. There must be one work item towards future graduation to a stable future.
- - Label the issue with `type/feature` and `experimental`.
+ - It should list development work items as checkboxes. There must be one work item towards future graduation to Beta.
+ - Label the issue with `/sig etcd`.
- Keep the issue open for tracking purposes until a decision is made on graduation.
-- Open a Pull Request (PR)
+- Open a [KEP](https://github.com/kubernetes/enhancements) Pull Request (PR).
+ - The KEP template can be simplified for etcd.
+ - It must provide clear graduation criteria for each stage.
+ - The KEP doc should reside in [keps/sig-etcd](https://github.com/kubernetes/enhancements/tree/master/keps/sig-etcd/)
+- Open Pull Requests (PRs) in [etcd](https://github.com/etcd-io/etcd)
- Provide unit tests. Integration tests are also recommended as possible.
- Provide robust e2e test coverage. If the feature being added is complicated or quickly needed, maintainers can decide to go with e2e tests for basic coverage initially and have robust coverage added at a later time before the feature graduation to the stable feature.
- Provide logs for proper debugging.
- Provide metrics and benchmarks as needed.
- - The Feature should be disabled by default.
- - Any configuration flags related to the implementation of the feature must be prefixed with `experimental` e.g. `--experimental-feature-name`.
+ - Add an Alpha [feature gate](https://etcd.io/docs/v3.6/feature-gates/).
+ - Any code changes or configuration flags related to the implementation of the feature must be gated with the feature gate e.g. `if cfg.ServerFeatureGate.Enabled(features.FeatureName)`.
- Add a CHANGELOG entry.
-- At least two maintainers must approve feature requirements and related code changes.
+- At least two maintainers must approve the KEP and related code changes.
-### Graduating an Experimental feature to Stable
+### Graduating a feature to the next stage
-It is important that experimental features don't get stuck in that stage. They should be revisited and moved to the stable stage following the graduation steps as described here.
-
-#### Locate graduation candidate
-Decide if an experimental feature is ready for graduation to the stable stage.
-- Find the issue that was used to enable the experimental feature initially. One way to find such issues is to search for issues with `type/feature` and `experimental` labels.
-- Fix any known open issues against the feature.
-- Make sure the feature was enabled for at least one previous release. Check the PR(s) reference from the issue to see when the feature-related code changes were merged.
+It is important that features don't get stuck in one stage. They should be revisited and moved to the next stage once they meet the graduation criteria listed in the KEP. A feature should stay at one stage for at least one release before being promoted.
#### Provide implementation
-If an experimental feature is found ready for graduation to the stable stage, open a Pull Request (PR) with the following changes.
-- Add robust e2e tests if not already provided.
-- Add a new stable feature flag identical to the experimental feature flag but without the `--experimental` prefix.
-- Deprecate the experimental feature following the [feature deprecation policy](#Deprecating-a-feature).
-- Implementation must ensure that both the graduated and deprecated experimental feature flags work as expected. Note that both these flags will co-exist for the timeframe described in the feature deprecation policy.
-- Enable the graduated feature by default if needed.
+
+If a feature is found ready for graduation to the next stage, open a Pull Request (PR) with the following changes.
+- Update the feature `PreRelease` stage in `server/features/etcd_features.go`.
+- Update the status in the original KEP issue.
At least two maintainers must approve the work. Patch releases should not be considered for graduation.
### Deprecating a feature
-#### Experimental
-An experimental feature deprecates when it graduates to the stable stage.
-- Add a deprecation message in the documentation of the experimental feature with a recommendation to use a related stable feature. e.g. `DEPRECATED. Use instead.`
-- Add a `deprecated` label in the issue that was initially used to enable the experimental feature.
+#### Alpha
+Alpha features can be removed without going through the deprecation process.
+- Remove the feature gate in `server/features/etcd_features.go`, and clean up all relevant code.
+- Close the original KEP issue with reasons to drop the feature.
+
+#### Beta and GA
+As the project evolves, a Beta/GA feature may sometimes need to be deprecated and removed. Such a situation should be handled using the steps below:
-#### Stable
-As the project evolves, a stable feature may sometimes need to be deprecated and removed. Such a situation should be handled using the steps below:
-- Create an issue for tracking purposes.
-- Add a deprecation message in the feature usage documentation before a planned release for feature deprecation. e.g. `To be deprecated in .`. If a new feature replaces the `To be deprecated` feature, then also provide a message saying so. e.g. `Use instead.`.
-- Deprecate the feature in the planned release with a message as part of the feature usage documentation. e.g. `DEPRECATED`. If a new feature replaces the deprecated feature, then also provide a message saying so. e.g. `DEPRECATED. Use instead.`.
-- Add a `deprecated` label in the related issue.
+- A Beta/GA feature can only be deprecated after at least 2 minor or major releases.
+- Update original KEP issue if it has not been closed or create a new etcd issue with reasons and steps to deprecate the feature.
+- Add the feature deprecation documentation in the release notes and feature gates documentation of the next minor/major release.
+- In the next minor/major release, set the feature gate to `{Default: false, PreRelease: featuregate.Deprecated, LockedToDefault: false}` in `server/features/etcd_features.go`. Deprecated feature gates must respond with a warning when used.
+ - If the feature has GAed, and the original gated codes has been cleaned up, add the disablement codes back with the feature gate.
+- In the minor/major release after the next, set the feature gate to `{Default: false, PreRelease: featuregate.Deprecated, LockedToDefault: true}` in `server/features/etcd_features.go`, and start cleaning the code.
-Remove the deprecated feature in the following release. Close any related issue(s). At least two maintainers must approve the work. Patch releases should not be considered for deprecation.
+At least two maintainers must approve the work. Patch releases should not be considered for deprecation.
diff --git a/Documentation/contributor-guide/modules.md b/Documentation/contributor-guide/modules.md
index dcee156747ca..cce3efc91a79 100644
--- a/Documentation/contributor-guide/modules.md
+++ b/Documentation/contributor-guide/modules.md
@@ -71,7 +71,7 @@ There are the following modules:
5. To trigger actions across all modules (e.g. auto-format all files), please
use/expand the following script:
```shell script
- % ./scripts/fix.sh
+ % make fix
```
### Future
diff --git a/Documentation/contributor-guide/prow_jobs.md b/Documentation/contributor-guide/prow_jobs.md
new file mode 100644
index 000000000000..d9025c8875cb
--- /dev/null
+++ b/Documentation/contributor-guide/prow_jobs.md
@@ -0,0 +1,115 @@
+# Analyzing Prow Job Resource Usage
+
+## 1. Introduction to Prow
+
+[Prow](https://docs.prow.k8s.io/docs/) is a Kubernetes based CI/CD system. Jobs can be triggered by various types of events and report their status to many different services. Prow provides GitHub automation through policy enforcement and chat-ops via `/command` interactions on pull requests (e.g., `/test`, `/approve`, `/retest`), enabling contributors to trigger jobs and manage workflows directly from GitHub comments.
+
+When a user comments `/ok-to-test`or `/retest,` on a Pull Request, GitHub sends a webhook to Prow's Kubernetes cluster. Visit this [site](https://docs.prow.k8s.io/docs/life-of-a-prow-job/) to further understand the lifecycle of a Prow job.
+This is where you can find all etcd Prow jobs [status](https://prow.k8s.io/?repo=etcd-io%2Fetcd)
+
+## 2. How Prow is used for etcd Testing
+
+etcd's CI is managed by [kubernetes/test-infra](https://github.com/kubernetes/test-infra), running Prow.
+
+When a pull request is submitted, or a `/command` is issued, the CI of etcd which managed by [kubernetes/test-infra](https://github.com/kubernetes/test-infra) uses Prow to run the tests. You can view all supported Prow [commands](https://prow.k8s.io/command-help).
+
+### Jobs Types
+
+The jobs [configuration](https://github.com/kubernetes/test-infra/tree/master/config/jobs/etcd) for etcd. Please see [ProwJob](https://docs.prow.k8s.io/docs/jobs/) docs for more info.
+
+There are 3 different job types:
+
+- Recurring jobs that regularly run etcd performance benchmarks, specifically targeting the put API, for the amd64 architecture.[etcd-benchmarks-periodics.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-benchmarks-periodic.yaml)
+- Presubmits jobs: Run on pull requests before code is merged, ensuring new changes do not break the build or tests. [etcd-operator-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-presubmits.yaml)
+- Postsubmits run after merging the etcd-io/etcd-operator code: [etcd-operator-postsubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-operator-postsubmits.yaml)
+- Periodic jobs are jobs that run automatically on a fixed schedule (such as every 4 hours, once a day, etc.), regardless of code changes or pull requests. They’re designed to continuously check the stability, compatibility, or performance of the project over time. [etcd-periodics.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-periodics.yaml)
+- Builds the etcd project for all main and release branches before merging any PR. [etcd-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-presubmits.yaml)
+- This file defines jobs that run automatically after code is merged (postsubmit) into the main or release branches of the etcd repository (etcd-io/etcd). [etcd-postsubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-postsubmits.yaml)
+- Test pull requests for the etcd-io/raft repository on certain branches (main and release-3.6)[etcd-raft-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-raft-presubmits.yaml)
+- Checks markdown formatting for website changes [etcd-website-presubmits.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/etcd-website-presubmits.yaml).
+- This file contains jobs that run after code is merged into the etcd-io/protodoc repository. [protodoc-presubmit.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/protodoc-postsubmits.yaml)
+- This file defines jobs that run on pull requests (before merge) for the etcd-io/protodoc repository.[protodoc-postsubmit.yaml](https://github.com/kubernetes/test-infra/blob/master/config/jobs/etcd/protodoc-presubmits.yaml)
+
+As an example, `pull-etcd-e2e-amd64` is one of the [presubmits](https://github.com/kubernetes/test-infra/blob/b21a1d3a72d5715ea7c9234cade21751847cfbe5/config/jobs/etcd/etcd-presubmits.yaml#L193). The job automatically runs end-to-end (e2e) tests on the amd64 architecture for every pull request to the etcd repository targeting the main, release-3.6, release-3.5, or release-3.4 branches. This is an example to its dashboard result [graph](https://prow.k8s.io/?repo=etcd-io%2Fetcd&type=presubmit&job=pull-etcd-e2e-amd64).
+
+Refer to [the test-infra Job Types documentation](https://github.com/kubernetes/test-infra/tree/master/config/jobs#job-types) to learn more about them.
+
+### How to Trigger Prow Running Tests
+
+These tests can be triggered when you leave a comment, like `/ok-to-test` (only triggered by an etcd-io member) or `/retest`, in PR [example](https://github.com/etcd-io/etcd/pull/20733#issuecomment-3341443205). `/ok-to-test` allows Prow to run tests on a pull request from a first-time contributor. `/retest` tells Prow to rerun any failed or flaky joobs on the pull request, useful if a previous test failed due to a transient issue.
+
+You can find all supported [commands](https://prow.k8s.io/command-help).
+
+## 3. Navigating Performance Dashboard (Grafana)
+
+Test-infra's Prow exposes Grafana dashboards to provide visibility into build resource usage (CPU, memory, number of running builds, etc.) for the Prow build cluster’s Kubernetes jobs. It is scoped via organization, repository, build identifier and time range filters.
+
+- GKE Dashboards: [https://monitoring-gke.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now](https://monitoring-gke.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now)
+- EKS Dashboards: [https://monitoring-eks.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now](https://monitoring-eks.prow.k8s.io/d/96Q8oOOZk/builds?orgId=1&refresh=30s&var-org=etcd-io&var-repo=etcd&var-build=All&from=now-7d&to=now)
+
+It is useful for a few reasons:
+
+1. Tuning resources: By drilling into each build-run, you can determine realistic memory & CPU requests and limits for that job‑type. This helps avoid waste or avoid failed builds hitting resource limits.
+
+2. Spotting anomalies: If one build suddenly used 8 GiB while normally this job uses 1 GiB, it may indicate a regression or mis‑configuration.
+
+3. Capacity planning: Seeing typical and peak usage helps cluster operators plan node sizes, scheduling, concurrency of builds, etc.
+
+4. Debugging performance issues: A build with unexpectedly high CPU or memory might be stuck, looping, or consuming resources inefficiently.
+
+### Panel: “Running / Pending Builds”
+
+Shows the number of builds that are in Running vs Pending states over time.
+Use it to track build backlog or concurrency — e.g., if the “Pending” line rises, builds may be waiting for resources.
+If the “Running” line fluctuates a lot or remains at some steady value, you can infer how many builds typically run in parallel.
+
+### Panel: “Memory Usage per Build”
+
+Shows memory usage over time for each build ID (each build listed in the legend at the bottom).
+The y‑axis shows memory use (e.g., in MiB / GiB).
+Use this to spot builds with unusually high memory usage — a spike indicates one build consumed many resources.
+
+### Panel: “CPU Usage per Build”
+
+Similar to the memory panel but shows CPU usage per build over time. Spikes in CPU usage may indicate heavy compute jobs, inefficiencies, or need for resource tuning.
+
+### Panel: "Resources"
+
+- Memory panel
+
+Green line (“used”): how much memory this build’s pod was using at each time point. Orange/Yellow line (“requested”): how much memory was requested (i.e., Kubernetes requests.memory) for that pod.
+Red line (“limit”): how much memory was limited (i.e., Kubernetes limits.memory) for that pod.
+Y‑axis: shows memory (GiB, MiB) over the build runtime.
+
+X‑axis: time of day/date.
+If the green “used” line is close to or hits the red “limit”, it means the build came close to its memory cap (risking OOM). If “used” is much lower than “requested”, you may be over‑allocating memory (waste).
+If the “requested” line is much higher than “used”, it suggests the job’s request could be tuned downward.
+
+- CPU panel
+
+Similar structure: green = actual usage, orange/yellow = requested CPU, red = CPU limit (if set).
+Y‑axis often in number of CPU cores or fraction thereof (e.g., 1.0 = one core).
+A green line with spikes may show bursts of CPU usage (e.g., build or compile phases) while idle periods show low usage.
+If CPU usage consistently saturates the limit, the job may be throttled or delayed. If usage is consistently far below request, tuning may reduce cost.
+
+## 3.1 Prow job categories (robustness, integration, static checks)
+
+- Static check:
+ - Description: Fast, deterministic checks (build, unit tests, linters, go vet/staticcheck, formatting, license/header checks, generated-code verification) that catch style, correctness and packaging problems early.
+ - When to run: Every PR as presubmits; quick feedback loop before running expensive tests.
+ - Example job patterns: pull-etcd-verify, pull-etcd-lint, pull-etcd-unit
+
+- Tests:
+ - Robustness:
+ - Description: Long-running, fault-injection and chaos-style end-to-end tests that validate etcd correctness and availability under failures (node crashes, network partitions, resource exhaustion, upgrades).
+ - When to run: Periodics for continuous coverage; run for PRs that touch consensus, storage, recovery, or upgrade paths.
+ - Example job patterns: pull-etcd-robustness, periodic-robustness
+
+ - Integration:
+ - Description: Functional end-to-end and cross-component tests that exercise real client/server interactions, snapshots/restore, upgrades and compatibility across OS/arch.
+ - When to run: Presubmits for PRs that change APIs, client behavior, or integration points; periodics for broad platform coverage.
+ - Example job patterns: pull-etcd-e2e-amd64, pull-etcd-integration
+
+## 4. Interpreting Metrics
+
+Some Prow components expose Prometheus metrics that can be used for monitoring and alerting. You can find metrics like the number of PRs in each Tide pool, a histogram of the number of PRs in each merge and various other metrics to this [site](https://github.com/kubernetes-sigs/prow/blob/main/site/content/en/docs/metrics/_index.md).
diff --git a/Documentation/contributor-guide/release.md b/Documentation/contributor-guide/release.md
index 266b500d1c4b..db315226e6d2 100644
--- a/Documentation/contributor-guide/release.md
+++ b/Documentation/contributor-guide/release.md
@@ -6,15 +6,17 @@ The procedure includes some manual steps for sanity checking, but it can probabl
## Release management
-The following pool of release candidates manages the release of each etcd major/minor version as well as manages patches
+Under the leadership of **James Blair** [@jmhbnz](https://github.com/jmhbnz) and **Ivan Valdes Castillo** [@ivanvc](https://github.com/ivanvc), the following pool of release candidates manages the release of each etcd major/minor version as well as manages patches
to each stable release branch. They are responsible for communicating the timelines and status of each release and
for ensuring the stability of the release branch.
- Benjamin Wang [@ahrtr](https://github.com/ahrtr)
+- Fu Wei [@fuweid](https://github.com/fuweid)
- James Blair [@jmhbnz](https://github.com/jmhbnz)
+- Ivan Valdes Castillo [@ivanvc](https://github.com/ivanvc)
- Marek Siarkowicz [@serathius](https://github.com/serathius)
- Sahdev Zala [@spzala](https://github.com/spzala)
-- Wenjia Zhang [@wenjiaswe](https://github.com/wenjiaswe)
+- Siyuan Zhang [@siyuanfoundation](https://github.com/siyuanfoundation)
All release version numbers follow the format of [semantic versioning 2.0.0](http://semver.org/).
@@ -68,10 +70,14 @@ which don't need to be executed before releasing each version.
### Release steps
+At least one day before the release:
+
1. Raise an issue to publish the release plan, e.g. [issues/17350](https://github.com/etcd-io/etcd/issues/17350).
-2. Raise a `kubernetes/org` pull request to temporarily elevate permissions for the GitHub release team.
-3. Once permissions are elevated, temporarily relax [branch protections](https://github.com/etcd-io/etcd/settings/branches) to allow pushing changes directly to `release-*` branches in GitHub.
-4. Verify you can pass the authentication to the image registries,
+2. Raise a `kubernetes/org` pull request ([example PR](https://github.com/kubernetes/org/pull/5582)) to ensure members of the release team are added to the [release github team](https://github.com/orgs/etcd-io/teams/release-etcd).
+
+On the day of the release:
+
+1. Verify you can pass the authentication to the image registries,
- `docker login gcr.io`
- `docker login quay.io`
- If the release person doesn't have access to 1password, one of the owners (@ahrtr, @ivanvc, @jmhbnz, @serathius) needs to share the password with them per [this guide](https://support.1password.com/share-items/). See rough steps below,
@@ -80,45 +86,56 @@ which don't need to be executed before releasing each version.
- Select `Password of quay.io`.
- Click `Share` on the top right, and set expiration as `1 hour` and only available to the release person using his/her email.
- Click `Copy Link` then send the link to the release person via slack or email.
-5. Clone the etcd repository and checkout the target branch,
+2. Clone the etcd repository and checkout the target branch,
- `git clone --branch release-3.X git@github.com:etcd-io/etcd.git`
-6. Run the release script under the repository's root directory, replacing `${VERSION}` with a value without the `v` prefix, i.e. `3.5.13`.
+3. Run the release script under the repository's root directory, replacing `${VERSION}` with a value without the `v` prefix, i.e. `3.5.13`.
- `DRY_RUN=false ./scripts/release.sh ${VERSION}`
- **NOTE:** When doing a pre-release (i.e., a version from the main branch, 3.6.0-alpha.2), you will need to explicitly set the branch to main:
- ```
+
+ ```bash
DRY_RUN=false BRANCH=main ./scripts/release.sh ${VERSION}
```
It generates all release binaries under the directory `/tmp/etcd-release-${VERSION}/etcd/release/` and images. Binaries are pushed to the Google Cloud bucket
under project `etcd-development`, and images are pushed to `quay.io` and `gcr.io`.
-7. Publish the release page on GitHub
+ - It is advisable to do a dry run before the actual release. This will create a `/tmp` directory. Do **NOT** forget to remove this directory before the actual release.
+
+ ```bash
+ DRY_RUN=true BRANCH=${BRANCH} ./scripts/release.sh ${VERSION}
+ ```
+
+4. Publish the release page on GitHub
- Open the **draft** release URL shown by the release script
- Click the pen button at the top right to edit the release
- Review that it looks correct, reviewing that the bottom checkboxes are checked depending on the
- release version (v3.4 no checkboxes, v3.5 has the set as latest release checkbox checked,
- v3.6 has the set as pre-release checkbox checked)
+ release version (v3.4 & v3.5 no checkboxes, v3.6 has the set as latest release checkbox checked,
+ v3.7 has the set as pre-release checkbox checked)
- Then, publish the release
-8. Announce to the etcd-dev googlegroup
+5. Announce to the etcd-dev googlegroup
Follow the format of previous release emails sent to etcd-dev@googlegroups.com, see an example below. After sending out the email, ask one of the mailing list maintainers to approve the email from the pending list. Additionally, label the release email as `Release`.
-```text
-Hello,
+ ```text
+ Hello,
+
+ etcd v3.4.30 is now public!
-etcd v3.4.30 is now public!
+ https://github.com/etcd-io/etcd/releases/tag/v3.4.30
-https://github.com/etcd-io/etcd/releases/tag/v3.4.30
+ Thanks to everyone who contributed to the release!
-Thanks to everyone who contributed to the release!
+ etcd team
+ ```
-etcd team
-```
+6. Update the changelog to reflect the correct release date.
+7. Paste the release link to the issue raised in Step 1 and close the issue.
+8. Raise a follow-up `kubernetes/org` pull request to return the GitHub release team to empty, least privilege state.
+9. Crease a new stable branch through `git push origin release-${VERSION_MAJOR}.${VERSION_MINOR}` if this is a new major or minor stable release.
+10. Re-generate a new password for quay.io if needed (e.g. shared to a contributor who isn't in the release team, and we should rotate the password at least once every 3 months).
+11. Bump the new etcd release in Kubernetes, refer to [Bump etcd Version in Kubernetes](bump_etcd_version_k8s.md).
-9. Update the changelog to reflect the correct release date.
-10. Paste the release link to the issue raised in Step 1 and close the issue.
-11. Restore standard branch protection settings and raise a follow-up `kubernetes/org` pull request to return to least privilege permissions.
-12. Crease a new stable branch through `git push origin release-${VERSION_MAJOR}.${VERSION_MINOR}` if this is a new major or minor stable release.
-13. Re-generate a new password for quay.io if needed (e.g. shared to a contributor who isn't in the release team, and we should rotate the password at least once every 3 months).
+- For etcd 3.6 patches, bump it to Kubernetes 1.34 and all newer minor versions (including `master` branch)
+- For etcd 3.5 patches, bump it to Kubernetes 1.33 and all older supported versions
#### Release known issues
diff --git a/Documentation/contributor-guide/roadmap.md b/Documentation/contributor-guide/roadmap.md
index c036e2eb45c4..9721a1b4ecce 100644
--- a/Documentation/contributor-guide/roadmap.md
+++ b/Documentation/contributor-guide/roadmap.md
@@ -12,18 +12,18 @@ Each item has an assigned priority. Refer to [priority definitions](https://gith
For a full list of tasks in `v3.6.0`, please see [milestone etcd-v3.6](https://github.com/etcd-io/etcd/milestone/38).
-| Title | Priority | Status | Note |
-|--------------------------------------------------------------------------------------------------------------------|----------|-------------|--------------------------------------------------------------------------------------------------------------|
-| [Support downgrade](https://github.com/etcd-io/etcd/issues/11716) | priority/important-soon | In progress | etcd will support downgrade starting from 3.6.0. But it will also support offline downgrade from 3.5 to 3.4. |
-| [StoreV2 deprecation](https://github.com/etcd-io/etcd/issues/12913) | priority/important-soon | In progress | This task will be covered in both 3.6 and 3.7. |
-| [Release raft 3.6.0](https://github.com/etcd-io/raft/issues/89) | priority/important-soon | Not started | etcd 3.6.0 will depends on raft 3.6.0 |
-| [Release bbolt 1.4.0](https://github.com/etcd-io/bbolt/issues/553) | priority/important-soon | Not started | etcd 3.6.0 will depends on bbolt 1.4.0 |
-| [Support /livez and /readyz endpoints](https://github.com/etcd-io/etcd/issues/16007) | priority/important-longterm | In progress | It provides clearer APIs, and can also work around the stalled writes issue |
+| Title | Priority | Status | Note |
+|--------------------------------------------------------------------------------------------------------------------|-----------------------------|-------------|--------------------------------------------------------------------------------------------------------------|
+| [Support downgrade](https://github.com/etcd-io/etcd/issues/11716) | priority/important-soon | Completed | etcd will support downgrade starting from 3.6.0. But it will also support offline downgrade from 3.5 to 3.4. |
+| [StoreV2 deprecation](https://github.com/etcd-io/etcd/issues/12913) | priority/important-soon | In progress | This task will be covered in both 3.6 and 3.7. |
+| [Release raft 3.6.0](https://github.com/etcd-io/raft/issues/89) | priority/important-soon | Completed | etcd 3.6.0 will depends on raft 3.6.0 |
+| [Release bbolt 1.4.0](https://github.com/etcd-io/bbolt/issues/553) | priority/important-soon | Completed | etcd 3.6.0 will depends on bbolt 1.4.0 |
+| [Support /livez and /readyz endpoints](https://github.com/etcd-io/etcd/issues/16007) | priority/important-longterm | Completed | It provides clearer APIs, and can also work around the stalled writes issue |
| [Bump gRPC](https://github.com/etcd-io/etcd/issues/16290) | priority/important-longterm | Completed | It isn't guaranteed to be resolved in 3.6, and might be postponed to 3.7 depending on the effort and risk. |
| [Deprecate grpc-gateway or bump it](https://github.com/etcd-io/etcd/issues/14499) | priority/important-longterm | Completed | It isn't guaranteed to be resolved in 3.6, and might be postponed to 3.7 depending on the effort and risk. |
| [bbolt: Add logger into bbolt](https://github.com/etcd-io/bbolt/issues/509) | priority/important-longterm | Completed | It's important to diagnose bbolt issues |
| [bbolt: Add surgery commands](https://github.com/etcd-io/bbolt/issues/370) | priority/important-longterm | Completed | Surgery commands are important for fixing corrupted db files |
-| [Evaluate and (Gradulate or deprecate/remove) experimental features](https://github.com/etcd-io/etcd/issues/16292) | priority/backlog | Not started | This task will be covered in both 3.6 and 3.7. |
+| [Evaluate and (Gradulate or deprecate/remove) experimental features](https://github.com/etcd-io/etcd/issues/16292) | priority/backlog | Not started | This task will be covered in both 3.6 and 3.7. |
## v3.7.0
@@ -32,8 +32,9 @@ For a full list of tasks in `v3.7.0`, please see [milestone etcd-v3.7](https://g
| Title | Priority | Note |
|-------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------------------|
| [StoreV2 deprecation](https://github.com/etcd-io/etcd/issues/12913) | P0 | Finish the remaining tasks 3.7. |
+| [Support range stream](https://github.com/etcd-io/etcd/issues/12342) | P0 | to be investigated & discussed. |
| [Refactor lease: Lease might be revoked by mistake by old leader](https://github.com/etcd-io/etcd/issues/15247) | P1 | to be investigated & discussed |
-| [Integrate raft's new feature (async write) into etcd](https://github.com/etcd-io/etcd/issues/16291) | P1 | It should improve the performance |
+| [Integrate raft's new feature (async write) into etcd](https://github.com/etcd-io/etcd/issues/16291) | P1 | It should improve the performance |
| [bbolt: Support customizing the bbolt rebalance threshold](https://github.com/etcd-io/bbolt/issues/422) | P2 | It may get rid of etcd's defragmentation. Both bbolt and etcd need to be changed. |
| [Evaluate and (graduate or deprecate/remove) experimental features](https://github.com/etcd-io/etcd/issues/16292) | P2 | Finish the remaining tasks 3.7. |
diff --git a/Documentation/contributor-guide/triage_prs.md b/Documentation/contributor-guide/triage_prs.md
index ffa0f7a1d8d8..f32e45f4cbf8 100644
--- a/Documentation/contributor-guide/triage_prs.md
+++ b/Documentation/contributor-guide/triage_prs.md
@@ -14,7 +14,7 @@ Following are a few example searches on PR for convenience:
## Scope
-These guidelines serve as a primary document for managing PRs in `etcd`. Everyone is welcome to help manage PRs but the work and responsibilities discussed in this document are created with `etcd` maintainers and active contributors in mind.
+These guidelines serve as a primary document for managing PRs and review policy in `etcd`. Everyone is welcome to help manage PRs but the work and responsibilities discussed in this document are created with `etcd` maintainers and active contributors in mind.
## Ensure tests are run
@@ -30,3 +30,32 @@ Reviewers are responsive in a timely fashion, but considering everyone is busy,
## Verify important labels are in place
Make sure that appropriate reviewers are added to the PR. Also, make sure that a milestone is identified. If any of these or other important labels are missing, add them. If a correct label cannot be decided, leave a comment for the maintainers to do so as needed.
+
+## Review policy
+
+To ensure code quality and shared ownership, this review policy applies to all pull requests (PRs).
+
+### Default rule
+
+PRs should get at least two approvals (/lgtm or GitHub review approval) before merging.
+
+Notes:
+
+* Approvals should come from a maintainer, reviewer, or submodule owner familiar with the relevant code or area.
+* If there’s disagreement, maintainers should discuss and agree before merging.
+
+### Exceptions for Less Impactful PRs
+
+For low-risk changes — such as:
+
+* CI workflows
+* Documentation
+* Comments
+
+The rule can be relaxed:
+
+* One approval is generally enough.
+
+However:
+
+* If the author is a maintainer, they should still get approval from another maintainer, reviewer, or submodule owner, even for minor changes.
diff --git a/Documentation/dev-guide/apispec/swagger/rpc.swagger.json b/Documentation/dev-guide/apispec/swagger/rpc.swagger.json
index 34fc55dff23f..371b6bcf81a4 100644
--- a/Documentation/dev-guide/apispec/swagger/rpc.swagger.json
+++ b/Documentation/dev-guide/apispec/swagger/rpc.swagger.json
@@ -45,7 +45,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -78,7 +78,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -111,7 +111,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -144,7 +144,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -177,7 +177,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -210,7 +210,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -243,7 +243,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -276,7 +276,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -309,7 +309,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -342,7 +342,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -375,7 +375,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -408,7 +408,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -441,7 +441,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -474,7 +474,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -495,7 +495,7 @@
},
"/v3/auth/user/grant": {
"post": {
- "summary": "UserGrant grants a role to a specified user.",
+ "summary": "UserGrantRole grants a role to a specified user.",
"operationId": "Auth_UserGrantRole",
"responses": {
"200": {
@@ -507,7 +507,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -540,7 +540,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -573,7 +573,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -606,7 +606,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -639,7 +639,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -672,7 +672,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -705,7 +705,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -738,7 +738,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -771,7 +771,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -805,7 +805,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -838,7 +838,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -871,7 +871,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -904,7 +904,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -937,7 +937,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -970,7 +970,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1003,7 +1003,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1037,7 +1037,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1070,7 +1070,7 @@
"$ref": "#/definitions/etcdserverpbLeaseKeepAliveResponse"
},
"error": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbLeaseKeepAliveResponse"
@@ -1079,7 +1079,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1113,7 +1113,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1146,7 +1146,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1179,7 +1179,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1212,7 +1212,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1245,7 +1245,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1278,7 +1278,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1311,7 +1311,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1344,7 +1344,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1377,7 +1377,7 @@
"$ref": "#/definitions/etcdserverpbSnapshotResponse"
},
"error": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbSnapshotResponse"
@@ -1386,7 +1386,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1419,7 +1419,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1452,7 +1452,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -1485,7 +1485,7 @@
"$ref": "#/definitions/etcdserverpbWatchResponse"
},
"error": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
},
"title": "Stream result of etcdserverpbWatchResponse"
@@ -1494,7 +1494,7 @@
"default": {
"description": "An unexpected error response.",
"schema": {
- "$ref": "#/definitions/googlerpcStatus"
+ "$ref": "#/definitions/googleRpcStatus"
}
}
},
@@ -3023,7 +3023,7 @@
}
}
},
- "googlerpcStatus": {
+ "googleRpcStatus": {
"type": "object",
"properties": {
"code": {
diff --git a/GOVERNANCE.md b/GOVERNANCE.md
index 6573d2f0194e..5ad999768577 100644
--- a/GOVERNANCE.md
+++ b/GOVERNANCE.md
@@ -36,7 +36,12 @@ weeks inactive voting period and as long as two maintainers are on board.
Changes in project governance could be initiated by opening a GitHub PR.
+## SIG-etcd Governance
+
+[SIG-etcd Governance] is documented in the Kubernetes/community repository.
+
[community membership]: /Documentation/contributor-guide/community-membership.md
[Code of Conduct]: /code-of-conduct.md
[contributor guide]: /CONTRIBUTING.md
[maintainers]: /OWNERS
+[SIG-etcd Governance]: https://github.com/kubernetes/community/blob/master/sig-etcd/charter.md#deviations-from-sig-governance
diff --git a/LICENSE b/LICENSE
index d64569567334..7a3ae1d408fa 100644
--- a/LICENSE
+++ b/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2013 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/Makefile b/Makefile
index 22ea1bcdf707..e6356e1f8192 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,27 @@
+REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
+
.PHONY: all
all: build
-include tests/robustness/makefile.mk
+include $(REPOSITORY_ROOT)/tests/robustness/Makefile
.PHONY: build
build:
GO_BUILD_FLAGS="${GO_BUILD_FLAGS} -v -mod=readonly" ./scripts/build.sh
+.PHONY: install-benchmark
+install-benchmark: build
+ifeq (, $(shell command -v benchmark))
+ @echo "Installing etcd benchmark tool..."
+ go install -v ./tools/benchmark
+else
+ @echo "benchmark tool already installed..."
+endif
+
+.PHONY: bench-put
+bench-put: build install-benchmark
+ @echo "Running benchmark: put $(ARGS)"
+ ./scripts/benchmark_test.sh put $(ARGS)
+
PLATFORMS=linux-amd64 linux-386 linux-arm linux-arm64 linux-ppc64le linux-s390x darwin-amd64 darwin-arm64 windows-amd64 windows-arm64
.PHONY: build-all
@@ -54,9 +70,10 @@ test-grpcproxy-e2e: build
test-e2e-release: build
PASSES="release e2e" ./scripts/test.sh $(GO_TEST_FLAGS)
+# When we release the first 3.7.0-alpha.0, we can remove `VERSION="3.7.99"` below.
.PHONY: test-release
test-release:
- PASSES="release_tests" ./scripts/test.sh $(GO_TEST_FLAGS)
+ PASSES="release_tests" VERSION="3.7.99" ./scripts/test.sh $(GO_TEST_FLAGS)
.PHONY: test-robustness
test-robustness:
@@ -79,18 +96,14 @@ fuzz:
# Static analysis
.PHONY: verify
-verify: verify-gofmt verify-bom verify-lint verify-dep verify-shellcheck verify-goword \
- verify-govet verify-license-header verify-mod-tidy \
+verify: verify-bom verify-lint verify-dep verify-shellcheck verify-mod-tidy \
verify-shellws verify-proto-annotations verify-genproto verify-yamllint \
- verify-govet-shadow verify-markdown-marker verify-go-versions
+ verify-markdown-marker verify-go-versions verify-gomodguard \
+ verify-go-workspace verify-grpc-experimental
.PHONY: fix
-fix: fix-bom fix-lint fix-yamllint sync-toolchain-directive
- ./scripts/fix.sh
-
-.PHONY: verify-gofmt
-verify-gofmt:
- PASSES="gofmt" ./scripts/test.sh
+fix: fix-mod-tidy fix-bom fix-lint fix-yamllint sync-toolchain-directive \
+ update-go-workspace fix-shell-ws
.PHONY: verify-bom
verify-bom:
@@ -98,7 +111,7 @@ verify-bom:
.PHONY: fix-bom
fix-bom:
- ./scripts/updatebom.sh
+ ./scripts/fix/bom.sh
.PHONY: verify-dep
verify-dep:
@@ -109,33 +122,29 @@ verify-lint: install-golangci-lint
PASSES="lint" ./scripts/test.sh
.PHONY: fix-lint
-fix-lint:
+fix-lint: install-golangci-lint
PASSES="lint_fix" ./scripts/test.sh
.PHONY: verify-shellcheck
verify-shellcheck:
PASSES="shellcheck" ./scripts/test.sh
-.PHONY: verify-goword
-verify-goword:
- PASSES="goword" ./scripts/test.sh
-
-.PHONY: verify-govet
-verify-govet:
- PASSES="govet" ./scripts/test.sh
-
-.PHONY: verify-license-header
-verify-license-header:
- PASSES="license_header" ./scripts/test.sh
-
.PHONY: verify-mod-tidy
verify-mod-tidy:
PASSES="mod_tidy" ./scripts/test.sh
+.PHONY: fix-mod-tidy
+fix-mod-tidy:
+ ./scripts/fix/mod-tidy.sh
+
.PHONY: verify-shellws
verify-shellws:
PASSES="shellws" ./scripts/test.sh
+.PHONY: fix-shell-ws
+fix-shell-ws:
+ ./scripts/fix/shell_ws.sh
+
.PHONY: verify-proto-annotations
verify-proto-annotations:
PASSES="proto_annotations" ./scripts/test.sh
@@ -146,7 +155,7 @@ verify-genproto:
.PHONY: verify-yamllint
verify-yamllint:
-ifeq (, $(shell which yamllint))
+ifeq (, $(shell command -v yamllint))
@echo "Installing yamllint..."
tmpdir=$$(mktemp -d); \
trap "rm -rf $$tmpdir" EXIT; \
@@ -158,38 +167,23 @@ else
yamllint --config-file tools/.yamllint .
endif
-.PHONY: verify-govet-shadow
-verify-govet-shadow:
- PASSES="govet_shadow" ./scripts/test.sh
-
.PHONY: verify-markdown-marker
verify-markdown-marker:
PASSES="markdown_marker" ./scripts/test.sh
-YAMLFMT_VERSION = $(shell cd tools/mod && go list -m -f '{{.Version}}' github.com/google/yamlfmt)
-
.PHONY: fix-yamllint
fix-yamllint:
-ifeq (, $(shell which yamlfmt))
- $(shell go install github.com/google/yamlfmt/cmd/yamlfmt@$(YAMLFMT_VERSION))
-endif
- yamlfmt -conf tools/.yamlfmt .
+ ./scripts/fix/yamllint.sh
.PHONY: run-govulncheck
run-govulncheck:
-ifeq (, $(shell which govulncheck))
- $(shell go install golang.org/x/vuln/cmd/govulncheck@latest)
-endif
PASSES="govuln" ./scripts/test.sh
# Tools
-GOLANGCI_LINT_VERSION = $(shell cd tools/mod && go list -m -f {{.Version}} github.com/golangci/golangci-lint/v2)
.PHONY: install-golangci-lint
install-golangci-lint:
-ifeq (, $(shell which golangci-lint))
- $(shell curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin $(GOLANGCI_LINT_VERSION))
-endif
+ ./scripts/verify_golangci-lint_version.sh
.PHONY: install-lazyfs
install-lazyfs: bin/lazyfs
@@ -222,6 +216,26 @@ clean:
verify-go-versions:
./scripts/verify_go_versions.sh
+.PHONY: verify-gomodguard
+verify-gomodguard:
+ PASSES="gomodguard" ./scripts/test.sh
+
+.PHONY: verify-go-workspace
+verify-go-workspace:
+ PASSES="go_workspace" ./scripts/test.sh
+
+.PHONY: verify-grpc-experimental
+verify-grpc-experimental:
+ ./scripts/verify_grpc_experimental.sh
+
.PHONY: sync-toolchain-directive
sync-toolchain-directive:
- ./scripts/sync_go_toolchain_directive.sh
+ ./scripts/fix/sync_go_toolchain_directive.sh
+
+.PHONY: markdown-diff-lint
+markdown-diff-lint:
+ ./scripts/markdown_diff_lint.sh
+
+.PHONY: update-go-workspace
+update-go-workspace:
+ ./scripts/update_go_workspace.sh
diff --git a/OWNERS b/OWNERS
index 393aea9670a9..5272d73d0e3b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,12 +1,19 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- - ahrtr # Benjamin Wang
- - jmhbnz # James Blair
- - serathius # Marek Siarkowicz
- - spzala # Sahdev Zala
- - wenjiaswe # Wenjia Zhang
-reviewers:
- - fuweid # Wei Fu
- - ivanvc # Ivan Valdes
- - siyuanfoundation # Siyuan Zhang
+ - sig-etcd-chairs # Defined in OWNERS_ALIASES
+ - sig-etcd-tech-leads # Defined in OWNERS_ALIASES
+ - spzala # Sahdev Zala
+emeritus_approvers:
+ - bdarnell # Ben Darnell
+ - fanminshi # Fanmin Shi
+ - gyuho # Gyuho Lee
+ - hexfusion # Sam Batschelet
+ - heyitsanthony # Anthony Romano
+ - jingyih # Jingyi Hu
+ - jpbetz # Joe Betz
+ - mitake # Hitoshi Mitake
+ - philips # Brandon Philips
+ - ptabor # Piotr Tabor
+ - wenjiaswe # Wenjia Zhang
+ - xiang90 # Xiang Li
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
new file mode 100644
index 000000000000..3ebdccec54e2
--- /dev/null
+++ b/OWNERS_ALIASES
@@ -0,0 +1,9 @@
+aliases:
+ sig-etcd-chairs:
+ - ivanvc # Ivan Valdes
+ - jmhbnz # James Blair
+ - siyuanfoundation # Siyuan Zhang
+ sig-etcd-tech-leads:
+ - ahrtr # Benjamin Wang
+ - fuweid # Wei Fu
+ - serathius # Marek Siarkowicz
diff --git a/README.md b/README.md
index 3f3bd97da460..1b1623708a19 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[](https://github.com/etcd-io/etcd/actions/workflows/tests.yaml)
[](https://github.com/etcd-io/etcd/actions/workflows/codeql-analysis.yml)
[](https://etcd.io/docs)
-[](https://godoc.org/github.com/etcd-io/etcd)
+[](https://godocs.io/go.etcd.io/etcd/v3)
[](https://github.com/etcd-io/etcd/releases)
[](https://github.com/etcd-io/etcd/blob/main/LICENSE)
[](https://scorecard.dev/viewer/?uri=github.com/etcd-io/etcd)
@@ -42,6 +42,18 @@ See [etcdctl][etcdctl] for a simple command line client.
[vulcand]: https://github.com/vulcand/vulcand
[etcdctl]: https://github.com/etcd-io/etcd/tree/main/etcdctl
+## Documentation
+
+The most common API documentation you'll need can be found here:
+
+* [go.etcd.io/etcd/api/v3](https://godocs.io/go.etcd.io/etcd/api/v3)
+* [go.etcd.io/etcd/client/pkg/v3](https://godocs.io/go.etcd.io/etcd/client/pkg/v3)
+* [go.etcd.io/etcd/client/v3](https://godocs.io/go.etcd.io/etcd/client/v3)
+* [go.etcd.io/etcd/etcdctl/v3](https://godocs.io/go.etcd.io/etcd/etcdctl/v3)
+* [go.etcd.io/etcd/pkg/v3](https://godocs.io/go.etcd.io/etcd/pkg/v3)
+* [go.etcd.io/etcd/raft/v3](https://godocs.io/go.etcd.io/etcd/raft/v3)
+* [go.etcd.io/etcd/server/v3](https://godocs.io/go.etcd.io/etcd/server/v3)
+
## Maintainers
[Maintainers](OWNERS) strive to shape an inclusive open source project culture where users are heard and contributors feel respected and empowered. Maintainers aim to build productive relationships across different companies and disciplines. Read more about [Maintainers role and responsibilities](Documentation/contributor-guide/community-membership.md#maintainers).
@@ -186,19 +198,7 @@ See [PR management](https://github.com/etcd-io/etcd/blob/main/Documentation/cont
## etcd Emeritus Maintainers
-These emeritus maintainers dedicated a part of their career to etcd and reviewed code, triaged bugs and pushed the project forward over a substantial period of time. Their contribution is greatly appreciated.
-
-* Fanmin Shi
-* Anthony Romano
-* Brandon Philips
-* Joe Betz
-* Gyuho Lee
-* Jingyi Hu
-* Xiang Li
-* Ben Darnell
-* Sam Batschelet
-* Piotr Tabor
-* Hitoshi Mitake
+etcd [emeritus maintainers](OWNERS) dedicated a part of their career to etcd and reviewed code, triaged bugs and pushed the project forward over a substantial period of time. Their contribution is greatly appreciated.
### License
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
new file mode 100644
index 000000000000..9e8f54285c6a
--- /dev/null
+++ b/THREAT_MODEL.md
@@ -0,0 +1,39 @@
+# etcd Threat Model
+
+This document defines the security assumptions and trust boundaries of the etcd project.
+
+Automated vulnerability scanners and security researchers MUST evaluate any security concern against these baseline boundaries.
+
+---
+
+## Security Boundaries & Assumptions
+
+etcd Server is a consistent, distributed key-value storage designed to operate as a secure backend.
+The baseline security boundaries are structured as follows:
+
+### The Network Boundary
+
+etcd Server assumes it is deployed within a strictly isolated, private network segment.
+It **must not** be exposed to untrusted networks or the public internet.
+Both the **etcd Client** and the **etcd Server** reside inside this protected perimeter.
+
+### The Client-to-Server Boundary
+
+etcd clients communicate with etcd Servers over Port 2379.
+This boundary requires **mTLS encryption**.
+Any client request must prove its identity at the transport layer using client certificates.
+
+### The Peer-to-Peer Boundary
+
+etcd Server members communicate with other cluster members over Port 2380 to run Raft consensus.
+This boundary must be strictly limited to authorized cluster members using dedicated, private peer certificates (mTLS).
+
+### The Host Execution Boundary
+
+etcd Server is compiled as a pure, statically linked Go binary (`CGO_ENABLED=0`).
+It does not dynamically load or link to dynamic system libraries (such as host C libraries or host transport encryption libraries) at runtime.
+
+### The Data Storage Boundary
+
+etcd Server writes data to the local storage subsystem exactly as received from the client.
+Data protection at rest is a client-side responsibility (e.g., client envelope encryption) or must be managed via disk filesystem encryption.
diff --git a/api/.gomodguard.yaml b/api/.gomodguard.yaml
new file mode 100644
index 000000000000..83a99c219c1f
--- /dev/null
+++ b/api/.gomodguard.yaml
@@ -0,0 +1,15 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/api/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/pkg/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/server/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/api/LICENSE b/api/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/api/LICENSE
+++ b/api/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/api/authpb/auth.pb.go b/api/authpb/auth.pb.go
index 37374c5a7111..cf2471095f48 100644
--- a/api/authpb/auth.pb.go
+++ b/api/authpb/auth.pb.go
@@ -1,1159 +1,383 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: auth.proto
package authpb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Permission_Type int32
const (
- READ Permission_Type = 0
- WRITE Permission_Type = 1
- READWRITE Permission_Type = 2
+ Permission_READ Permission_Type = 0
+ Permission_WRITE Permission_Type = 1
+ Permission_READWRITE Permission_Type = 2
)
-var Permission_Type_name = map[int32]string{
- 0: "READ",
- 1: "WRITE",
- 2: "READWRITE",
-}
+// Enum value maps for Permission_Type.
+var (
+ Permission_Type_name = map[int32]string{
+ 0: "READ",
+ 1: "WRITE",
+ 2: "READWRITE",
+ }
+ Permission_Type_value = map[string]int32{
+ "READ": 0,
+ "WRITE": 1,
+ "READWRITE": 2,
+ }
+)
-var Permission_Type_value = map[string]int32{
- "READ": 0,
- "WRITE": 1,
- "READWRITE": 2,
+func (x Permission_Type) Enum() *Permission_Type {
+ p := new(Permission_Type)
+ *p = x
+ return p
}
func (x Permission_Type) String() string {
- return proto.EnumName(Permission_Type_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
-func (Permission_Type) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_8bbd6f3875b0e874, []int{2, 0}
+func (Permission_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_auth_proto_enumTypes[0].Descriptor()
}
-type UserAddOptions struct {
- NoPassword bool `protobuf:"varint,1,opt,name=no_password,json=noPassword,proto3" json:"no_password,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (Permission_Type) Type() protoreflect.EnumType {
+ return &file_auth_proto_enumTypes[0]
}
-func (m *UserAddOptions) Reset() { *m = UserAddOptions{} }
-func (m *UserAddOptions) String() string { return proto.CompactTextString(m) }
-func (*UserAddOptions) ProtoMessage() {}
-func (*UserAddOptions) Descriptor() ([]byte, []int) {
- return fileDescriptor_8bbd6f3875b0e874, []int{0}
-}
-func (m *UserAddOptions) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *UserAddOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_UserAddOptions.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *UserAddOptions) XXX_Merge(src proto.Message) {
- xxx_messageInfo_UserAddOptions.Merge(m, src)
-}
-func (m *UserAddOptions) XXX_Size() int {
- return m.Size()
-}
-func (m *UserAddOptions) XXX_DiscardUnknown() {
- xxx_messageInfo_UserAddOptions.DiscardUnknown(m)
+func (x Permission_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
-var xxx_messageInfo_UserAddOptions proto.InternalMessageInfo
-
-// User is a single entry in the bucket authUsers
-type User struct {
- Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- Password []byte `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
- Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
- Options *UserAddOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use Permission_Type.Descriptor instead.
+func (Permission_Type) EnumDescriptor() ([]byte, []int) {
+ return file_auth_proto_rawDescGZIP(), []int{2, 0}
}
-func (m *User) Reset() { *m = User{} }
-func (m *User) String() string { return proto.CompactTextString(m) }
-func (*User) ProtoMessage() {}
-func (*User) Descriptor() ([]byte, []int) {
- return fileDescriptor_8bbd6f3875b0e874, []int{1}
-}
-func (m *User) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_User.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *User) XXX_Merge(src proto.Message) {
- xxx_messageInfo_User.Merge(m, src)
-}
-func (m *User) XXX_Size() int {
- return m.Size()
-}
-func (m *User) XXX_DiscardUnknown() {
- xxx_messageInfo_User.DiscardUnknown(m)
+type UserAddOptions struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NoPassword bool `protobuf:"varint,1,opt,name=no_password,json=noPassword,proto3" json:"no_password,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_User proto.InternalMessageInfo
-
-// Permission is a single entity
-type Permission struct {
- PermType Permission_Type `protobuf:"varint,1,opt,name=permType,proto3,enum=authpb.Permission_Type" json:"permType,omitempty"`
- Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
- RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *UserAddOptions) Reset() {
+ *x = UserAddOptions{}
+ mi := &file_auth_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Permission) Reset() { *m = Permission{} }
-func (m *Permission) String() string { return proto.CompactTextString(m) }
-func (*Permission) ProtoMessage() {}
-func (*Permission) Descriptor() ([]byte, []int) {
- return fileDescriptor_8bbd6f3875b0e874, []int{2}
-}
-func (m *Permission) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
+func (x *UserAddOptions) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Permission) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Permission.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+
+func (*UserAddOptions) ProtoMessage() {}
+
+func (x *UserAddOptions) ProtoReflect() protoreflect.Message {
+ mi := &file_auth_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *Permission) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Permission.Merge(m, src)
-}
-func (m *Permission) XXX_Size() int {
- return m.Size()
-}
-func (m *Permission) XXX_DiscardUnknown() {
- xxx_messageInfo_Permission.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_Permission proto.InternalMessageInfo
-
-// Role is a single entry in the bucket authRoles
-type Role struct {
- Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- KeyPermission []*Permission `protobuf:"bytes,2,rep,name=keyPermission,proto3" json:"keyPermission,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use UserAddOptions.ProtoReflect.Descriptor instead.
+func (*UserAddOptions) Descriptor() ([]byte, []int) {
+ return file_auth_proto_rawDescGZIP(), []int{0}
}
-func (m *Role) Reset() { *m = Role{} }
-func (m *Role) String() string { return proto.CompactTextString(m) }
-func (*Role) ProtoMessage() {}
-func (*Role) Descriptor() ([]byte, []int) {
- return fileDescriptor_8bbd6f3875b0e874, []int{3}
-}
-func (m *Role) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Role) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Role.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *UserAddOptions) GetNoPassword() bool {
+ if x != nil {
+ return x.NoPassword
}
-}
-func (m *Role) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Role.Merge(m, src)
-}
-func (m *Role) XXX_Size() int {
- return m.Size()
-}
-func (m *Role) XXX_DiscardUnknown() {
- xxx_messageInfo_Role.DiscardUnknown(m)
+ return false
}
-var xxx_messageInfo_Role proto.InternalMessageInfo
-
-func init() {
- proto.RegisterEnum("authpb.Permission_Type", Permission_Type_name, Permission_Type_value)
- proto.RegisterType((*UserAddOptions)(nil), "authpb.UserAddOptions")
- proto.RegisterType((*User)(nil), "authpb.User")
- proto.RegisterType((*Permission)(nil), "authpb.Permission")
- proto.RegisterType((*Role)(nil), "authpb.Role")
+// User is a single entry in the bucket authUsers
+type User struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Password []byte `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
+ Options *UserAddOptions `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func init() { proto.RegisterFile("auth.proto", fileDescriptor_8bbd6f3875b0e874) }
-
-var fileDescriptor_8bbd6f3875b0e874 = []byte{
- // 359 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xcf, 0x4e, 0xc2, 0x40,
- 0x10, 0xc6, 0xbb, 0xb4, 0x60, 0x3b, 0x08, 0x21, 0x1b, 0xa2, 0x0d, 0xc6, 0xda, 0xf4, 0xd4, 0x78,
- 0x68, 0x15, 0x0e, 0x7a, 0xc5, 0xc8, 0xc1, 0x93, 0x64, 0x83, 0x31, 0xf1, 0x42, 0x8a, 0xdd, 0xd4,
- 0x06, 0xd8, 0x6d, 0xda, 0xaa, 0xe1, 0xe2, 0x73, 0x78, 0xf0, 0x81, 0x38, 0xf2, 0x08, 0x82, 0x2f,
- 0x62, 0xba, 0xcb, 0x9f, 0x10, 0x3d, 0xed, 0x37, 0xdf, 0x7c, 0x33, 0xfb, 0xcb, 0x2e, 0x40, 0xf0,
- 0x9a, 0xbf, 0x78, 0x49, 0xca, 0x73, 0x8e, 0x2b, 0x85, 0x4e, 0x46, 0xad, 0x66, 0xc4, 0x23, 0x2e,
- 0x2c, 0xbf, 0x50, 0xb2, 0xeb, 0x5c, 0x42, 0xfd, 0x21, 0xa3, 0x69, 0x37, 0x0c, 0xef, 0x93, 0x3c,
- 0xe6, 0x2c, 0xc3, 0x67, 0x50, 0x65, 0x7c, 0x98, 0x04, 0x59, 0xf6, 0xce, 0xd3, 0xd0, 0x44, 0x36,
- 0x72, 0x75, 0x02, 0x8c, 0xf7, 0xd7, 0x8e, 0xf3, 0x01, 0x5a, 0x31, 0x82, 0x31, 0x68, 0x2c, 0x98,
- 0x52, 0x91, 0x38, 0x24, 0x42, 0xe3, 0x16, 0xe8, 0xdb, 0xc9, 0x92, 0xf0, 0xb7, 0x35, 0x6e, 0x42,
- 0x39, 0xe5, 0x13, 0x9a, 0x99, 0xaa, 0xad, 0xba, 0x06, 0x91, 0x05, 0xbe, 0x80, 0x03, 0x2e, 0x6f,
- 0x36, 0x35, 0x1b, 0xb9, 0xd5, 0xf6, 0x91, 0x27, 0x81, 0xbd, 0x7d, 0x2e, 0xb2, 0x89, 0x39, 0x5f,
- 0x08, 0xa0, 0x4f, 0xd3, 0x69, 0x9c, 0x65, 0x31, 0x67, 0xb8, 0x03, 0x7a, 0x42, 0xd3, 0xe9, 0x60,
- 0x96, 0x48, 0x94, 0x7a, 0xfb, 0x78, 0xb3, 0x61, 0x97, 0xf2, 0x8a, 0x36, 0xd9, 0x06, 0x71, 0x03,
- 0xd4, 0x31, 0x9d, 0xad, 0x11, 0x0b, 0x89, 0x4f, 0xc0, 0x48, 0x03, 0x16, 0xd1, 0x21, 0x65, 0xa1,
- 0xa9, 0x4a, 0x74, 0x61, 0xf4, 0x58, 0xe8, 0x9c, 0x83, 0x26, 0xc6, 0x74, 0xd0, 0x48, 0xaf, 0x7b,
- 0xdb, 0x50, 0xb0, 0x01, 0xe5, 0x47, 0x72, 0x37, 0xe8, 0x35, 0x10, 0xae, 0x81, 0x51, 0x98, 0xb2,
- 0x2c, 0x39, 0x03, 0xd0, 0x08, 0x9f, 0xd0, 0x7f, 0x9f, 0xe7, 0x1a, 0x6a, 0x63, 0x3a, 0xdb, 0x61,
- 0x99, 0x25, 0x5b, 0x75, 0xab, 0x6d, 0xfc, 0x17, 0x98, 0xec, 0x07, 0x6f, 0xae, 0xe6, 0x4b, 0x4b,
- 0x59, 0x2c, 0x2d, 0x65, 0xbe, 0xb2, 0xd0, 0x62, 0x65, 0xa1, 0xef, 0x95, 0x85, 0x3e, 0x7f, 0x2c,
- 0xe5, 0xe9, 0x34, 0xe2, 0x1e, 0xcd, 0x9f, 0x43, 0x2f, 0xe6, 0x7e, 0x71, 0xfa, 0x41, 0x12, 0xfb,
- 0x6f, 0x1d, 0x5f, 0xae, 0x1c, 0x55, 0xc4, 0x3f, 0x77, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x61,
- 0x5a, 0xfe, 0x48, 0x13, 0x02, 0x00, 0x00,
+func (x *User) Reset() {
+ *x = User{}
+ mi := &file_auth_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *UserAddOptions) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *User) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *UserAddOptions) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*User) ProtoMessage() {}
-func (m *UserAddOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.NoPassword {
- i--
- if m.NoPassword {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
+func (x *User) ProtoReflect() protoreflect.Message {
+ mi := &file_auth_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x8
+ return ms
}
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func (m *User) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+// Deprecated: Use User.ProtoReflect.Descriptor instead.
+func (*User) Descriptor() ([]byte, []int) {
+ return file_auth_proto_rawDescGZIP(), []int{1}
}
-func (m *User) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *User) GetName() []byte {
+ if x != nil {
+ return x.Name
+ }
+ return nil
}
-func (m *User) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
+func (x *User) GetPassword() []byte {
+ if x != nil {
+ return x.Password
}
- if m.Options != nil {
- {
- size, err := m.Options.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintAuth(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x22
- }
- if len(m.Roles) > 0 {
- for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Roles[iNdEx])
- copy(dAtA[i:], m.Roles[iNdEx])
- i = encodeVarintAuth(dAtA, i, uint64(len(m.Roles[iNdEx])))
- i--
- dAtA[i] = 0x1a
- }
- }
- if len(m.Password) > 0 {
- i -= len(m.Password)
- copy(dAtA[i:], m.Password)
- i = encodeVarintAuth(dAtA, i, uint64(len(m.Password)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintAuth(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+ return nil
}
-func (m *Permission) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *User) GetRoles() []string {
+ if x != nil {
+ return x.Roles
}
- return dAtA[:n], nil
+ return nil
}
-func (m *Permission) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *User) GetOptions() *UserAddOptions {
+ if x != nil {
+ return x.Options
+ }
+ return nil
}
-func (m *Permission) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintAuth(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x1a
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintAuth(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0x12
- }
- if m.PermType != 0 {
- i = encodeVarintAuth(dAtA, i, uint64(m.PermType))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
+// Permission is a single entity
+type Permission struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ PermType Permission_Type `protobuf:"varint,1,opt,name=permType,proto3,enum=authpb.Permission_Type" json:"permType,omitempty"`
+ Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+ RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Role) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *Permission) Reset() {
+ *x = Permission{}
+ mi := &file_auth_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Role) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *Permission) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Role) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.KeyPermission) > 0 {
- for iNdEx := len(m.KeyPermission) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.KeyPermission[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintAuth(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
+func (*Permission) ProtoMessage() {}
+
+func (x *Permission) ProtoReflect() protoreflect.Message {
+ mi := &file_auth_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintAuth(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func encodeVarintAuth(dAtA []byte, offset int, v uint64) int {
- offset -= sovAuth(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *UserAddOptions) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.NoPassword {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use Permission.ProtoReflect.Descriptor instead.
+func (*Permission) Descriptor() ([]byte, []int) {
+ return file_auth_proto_rawDescGZIP(), []int{2}
}
-func (m *User) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovAuth(uint64(l))
+func (x *Permission) GetPermType() Permission_Type {
+ if x != nil {
+ return x.PermType
}
- l = len(m.Password)
- if l > 0 {
- n += 1 + l + sovAuth(uint64(l))
- }
- if len(m.Roles) > 0 {
- for _, s := range m.Roles {
- l = len(s)
- n += 1 + l + sovAuth(uint64(l))
- }
- }
- if m.Options != nil {
- l = m.Options.Size()
- n += 1 + l + sovAuth(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return Permission_READ
}
-func (m *Permission) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.PermType != 0 {
- n += 1 + sovAuth(uint64(m.PermType))
- }
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovAuth(uint64(l))
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 1 + l + sovAuth(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
+func (x *Permission) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
- return n
+ return nil
}
-func (m *Role) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovAuth(uint64(l))
- }
- if len(m.KeyPermission) > 0 {
- for _, e := range m.KeyPermission {
- l = e.Size()
- n += 1 + l + sovAuth(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
+func (x *Permission) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
- return n
+ return nil
}
-func sovAuth(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
+// Role is a single entry in the bucket authRoles
+type Role struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ KeyPermission []*Permission `protobuf:"bytes,2,rep,name=keyPermission,proto3" json:"keyPermission,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func sozAuth(x uint64) (n int) {
- return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+
+func (x *Role) Reset() {
+ *x = Role{}
+ mi := &file_auth_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *UserAddOptions) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: UserAddOptions: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: UserAddOptions: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field NoPassword", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.NoPassword = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipAuth(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthAuth
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *Role) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *User) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: User: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: User: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Password = append(m.Password[:0], dAtA[iNdEx:postIndex]...)
- if m.Password == nil {
- m.Password = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Options == nil {
- m.Options = &UserAddOptions{}
- }
- if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipAuth(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthAuth
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
+
+func (*Role) ProtoMessage() {}
+
+func (x *Role) ProtoReflect() protoreflect.Message {
+ mi := &file_auth_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
+ return mi.MessageOf(x)
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+// Deprecated: Use Role.ProtoReflect.Descriptor instead.
+func (*Role) Descriptor() ([]byte, []int) {
+ return file_auth_proto_rawDescGZIP(), []int{3}
}
-func (m *Permission) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Permission: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Permission: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PermType", wireType)
- }
- m.PermType = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.PermType |= Permission_Type(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipAuth(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthAuth
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Role) GetName() []byte {
+ if x != nil {
+ return x.Name
}
return nil
}
-func (m *Role) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Role: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Role: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field KeyPermission", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthAuth
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthAuth
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.KeyPermission = append(m.KeyPermission, &Permission{})
- if err := m.KeyPermission[len(m.KeyPermission)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipAuth(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthAuth
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Role) GetKeyPermission() []*Permission {
+ if x != nil {
+ return x.KeyPermission
}
return nil
}
-func skipAuth(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowAuth
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthAuth
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupAuth
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthAuth
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+
+var File_auth_proto protoreflect.FileDescriptor
+
+const file_auth_proto_rawDesc = "" +
+ "\n" +
+ "\n" +
+ "auth.proto\x12\x06authpb\"1\n" +
+ "\x0eUserAddOptions\x12\x1f\n" +
+ "\vno_password\x18\x01 \x01(\bR\n" +
+ "noPassword\"~\n" +
+ "\x04User\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\x12\x1a\n" +
+ "\bpassword\x18\x02 \x01(\fR\bpassword\x12\x14\n" +
+ "\x05roles\x18\x03 \x03(\tR\x05roles\x120\n" +
+ "\aoptions\x18\x04 \x01(\v2\x16.authpb.UserAddOptionsR\aoptions\"\x9c\x01\n" +
+ "\n" +
+ "Permission\x123\n" +
+ "\bpermType\x18\x01 \x01(\x0e2\x17.authpb.Permission.TypeR\bpermType\x12\x10\n" +
+ "\x03key\x18\x02 \x01(\fR\x03key\x12\x1b\n" +
+ "\trange_end\x18\x03 \x01(\fR\brangeEnd\"*\n" +
+ "\x04Type\x12\b\n" +
+ "\x04READ\x10\x00\x12\t\n" +
+ "\x05WRITE\x10\x01\x12\r\n" +
+ "\tREADWRITE\x10\x02\"T\n" +
+ "\x04Role\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\x128\n" +
+ "\rkeyPermission\x18\x02 \x03(\v2\x12.authpb.PermissionR\rkeyPermissionB\x1fZ\x1dgo.etcd.io/etcd/api/v3/authpbb\x06proto3"
var (
- ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupAuth = fmt.Errorf("proto: unexpected end of group")
+ file_auth_proto_rawDescOnce sync.Once
+ file_auth_proto_rawDescData []byte
)
+
+func file_auth_proto_rawDescGZIP() []byte {
+ file_auth_proto_rawDescOnce.Do(func() {
+ file_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_auth_proto_rawDesc), len(file_auth_proto_rawDesc)))
+ })
+ return file_auth_proto_rawDescData
+}
+
+var file_auth_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_auth_proto_goTypes = []any{
+ (Permission_Type)(0), // 0: authpb.Permission.Type
+ (*UserAddOptions)(nil), // 1: authpb.UserAddOptions
+ (*User)(nil), // 2: authpb.User
+ (*Permission)(nil), // 3: authpb.Permission
+ (*Role)(nil), // 4: authpb.Role
+}
+var file_auth_proto_depIdxs = []int32{
+ 1, // 0: authpb.User.options:type_name -> authpb.UserAddOptions
+ 0, // 1: authpb.Permission.permType:type_name -> authpb.Permission.Type
+ 3, // 2: authpb.Role.keyPermission:type_name -> authpb.Permission
+ 3, // [3:3] is the sub-list for method output_type
+ 3, // [3:3] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_auth_proto_init() }
+func file_auth_proto_init() {
+ if File_auth_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_auth_proto_rawDesc), len(file_auth_proto_rawDesc)),
+ NumEnums: 1,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_auth_proto_goTypes,
+ DependencyIndexes: file_auth_proto_depIdxs,
+ EnumInfos: file_auth_proto_enumTypes,
+ MessageInfos: file_auth_proto_msgTypes,
+ }.Build()
+ File_auth_proto = out.File
+ file_auth_proto_goTypes = nil
+ file_auth_proto_depIdxs = nil
+}
diff --git a/api/authpb/auth.proto b/api/authpb/auth.proto
index 5a7856bb73dc..41bcaaecbf18 100644
--- a/api/authpb/auth.proto
+++ b/api/authpb/auth.proto
@@ -1,16 +1,8 @@
syntax = "proto3";
package authpb;
-import "gogoproto/gogo.proto";
-
option go_package = "go.etcd.io/etcd/api/v3/authpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-option (gogoproto.goproto_enum_prefix_all) = false;
-
message UserAddOptions {
bool no_password = 1;
};
diff --git a/api/authpb/deprecated.go b/api/authpb/deprecated.go
new file mode 100644
index 000000000000..df853b7b497a
--- /dev/null
+++ b/api/authpb/deprecated.go
@@ -0,0 +1,27 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 authpb
+
+const (
+ // READ is an alias of Permission_READ
+ // Deprecated: use Permission_READ instead. Will be removed in v3.8.
+ READ = Permission_READ
+ // WRITE is an alias of Permission_WRITE
+ // Deprecated: use Permission_WRITE instead. Will be removed in v3.8.
+ WRITE = Permission_WRITE
+ // READWRITE is an alias of Permission_READWRITE
+ // Deprecated: use Permission_READWRITE instead. Will be removed in v3.8.
+ READWRITE = Permission_READWRITE
+)
diff --git a/api/etcdserverpb/etcdserver.pb.go b/api/etcdserverpb/etcdserver.pb.go
index eaefa2d20888..14dd9d114c15 100644
--- a/api/etcdserverpb/etcdserver.pb.go
+++ b/api/etcdserverpb/etcdserver.pb.go
@@ -1,1004 +1,132 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: etcdserver.proto
package etcdserverpb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
-
-type Request struct {
- ID uint64 `protobuf:"varint,1,opt,name=ID" json:"ID"`
- Method string `protobuf:"bytes,2,opt,name=Method" json:"Method"`
- Path string `protobuf:"bytes,3,opt,name=Path" json:"Path"`
- Val string `protobuf:"bytes,4,opt,name=Val" json:"Val"`
- Dir bool `protobuf:"varint,5,opt,name=Dir" json:"Dir"`
- PrevValue string `protobuf:"bytes,6,opt,name=PrevValue" json:"PrevValue"`
- PrevIndex uint64 `protobuf:"varint,7,opt,name=PrevIndex" json:"PrevIndex"`
- PrevExist *bool `protobuf:"varint,8,opt,name=PrevExist" json:"PrevExist,omitempty"`
- Expiration int64 `protobuf:"varint,9,opt,name=Expiration" json:"Expiration"`
- Wait bool `protobuf:"varint,10,opt,name=Wait" json:"Wait"`
- Since uint64 `protobuf:"varint,11,opt,name=Since" json:"Since"`
- Recursive bool `protobuf:"varint,12,opt,name=Recursive" json:"Recursive"`
- Sorted bool `protobuf:"varint,13,opt,name=Sorted" json:"Sorted"`
- Quorum bool `protobuf:"varint,14,opt,name=Quorum" json:"Quorum"`
- Time int64 `protobuf:"varint,15,opt,name=Time" json:"Time"`
- Stream bool `protobuf:"varint,16,opt,name=Stream" json:"Stream"`
- Refresh *bool `protobuf:"varint,17,opt,name=Refresh" json:"Refresh,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *Request) Reset() { *m = Request{} }
-func (m *Request) String() string { return proto.CompactTextString(m) }
-func (*Request) ProtoMessage() {}
-func (*Request) Descriptor() ([]byte, []int) {
- return fileDescriptor_09ffbeb3bebbce7e, []int{0}
-}
-func (m *Request) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Request.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Request) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Request.Merge(m, src)
-}
-func (m *Request) XXX_Size() int {
- return m.Size()
-}
-func (m *Request) XXX_DiscardUnknown() {
- xxx_messageInfo_Request.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Request proto.InternalMessageInfo
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Metadata struct {
- NodeID uint64 `protobuf:"varint,1,opt,name=NodeID" json:"NodeID"`
- ClusterID uint64 `protobuf:"varint,2,opt,name=ClusterID" json:"ClusterID"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NodeID *uint64 `protobuf:"varint,1,opt,name=NodeID" json:"NodeID,omitempty"`
+ ClusterID *uint64 `protobuf:"varint,2,opt,name=ClusterID" json:"ClusterID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Metadata) Reset() { *m = Metadata{} }
-func (m *Metadata) String() string { return proto.CompactTextString(m) }
-func (*Metadata) ProtoMessage() {}
-func (*Metadata) Descriptor() ([]byte, []int) {
- return fileDescriptor_09ffbeb3bebbce7e, []int{1}
-}
-func (m *Metadata) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Metadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Metadata.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Metadata) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Metadata.Merge(m, src)
-}
-func (m *Metadata) XXX_Size() int {
- return m.Size()
-}
-func (m *Metadata) XXX_DiscardUnknown() {
- xxx_messageInfo_Metadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Metadata proto.InternalMessageInfo
-
-func init() {
- proto.RegisterType((*Request)(nil), "etcdserverpb.Request")
- proto.RegisterType((*Metadata)(nil), "etcdserverpb.Metadata")
+func (x *Metadata) Reset() {
+ *x = Metadata{}
+ mi := &file_etcdserver_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func init() { proto.RegisterFile("etcdserver.proto", fileDescriptor_09ffbeb3bebbce7e) }
-
-var fileDescriptor_09ffbeb3bebbce7e = []byte{
- // 402 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0xd2, 0x41, 0xef, 0xd2, 0x30,
- 0x14, 0x00, 0x70, 0x0a, 0xfb, 0xff, 0x81, 0x8a, 0x8a, 0x0d, 0x31, 0x2f, 0xc4, 0xcc, 0x05, 0x3d,
- 0xec, 0xc4, 0x0e, 0x9e, 0xbc, 0xe2, 0x38, 0x2c, 0x11, 0x83, 0xc3, 0x60, 0xe2, 0xad, 0xb2, 0x27,
- 0x34, 0x01, 0x3a, 0xbb, 0x6e, 0xe1, 0x1b, 0xf8, 0x15, 0xfc, 0x48, 0x1c, 0xfd, 0x04, 0x46, 0xf1,
- 0x8b, 0x98, 0x8e, 0x8d, 0x55, 0x4f, 0x5b, 0x7e, 0xef, 0xf5, 0xf5, 0xb5, 0x7d, 0x74, 0x88, 0x7a,
- 0x93, 0x64, 0xa8, 0x0a, 0x54, 0xd3, 0x54, 0x49, 0x2d, 0xd9, 0xa0, 0x91, 0xf4, 0xf3, 0x78, 0xb4,
- 0x95, 0x5b, 0x59, 0x06, 0x02, 0xf3, 0x77, 0xcd, 0x99, 0x7c, 0x73, 0x68, 0x37, 0xc6, 0xaf, 0x39,
- 0x66, 0x9a, 0x8d, 0x68, 0x3b, 0x0a, 0x81, 0x78, 0xc4, 0x77, 0x66, 0xce, 0xf9, 0xe7, 0xf3, 0x56,
- 0xdc, 0x8e, 0x42, 0xf6, 0x8c, 0xde, 0x2f, 0x50, 0xef, 0x64, 0x02, 0x6d, 0x8f, 0xf8, 0xfd, 0x2a,
- 0x52, 0x19, 0x03, 0xea, 0x2c, 0xb9, 0xde, 0x41, 0xc7, 0x8a, 0x95, 0xc2, 0x9e, 0xd2, 0xce, 0x9a,
- 0xef, 0xc1, 0xb1, 0x02, 0x06, 0x8c, 0x87, 0x42, 0xc1, 0x9d, 0x47, 0xfc, 0x5e, 0xed, 0xa1, 0x50,
- 0x6c, 0x42, 0xfb, 0x4b, 0x85, 0xc5, 0x9a, 0xef, 0x73, 0x84, 0x7b, 0x6b, 0x55, 0xc3, 0x75, 0x4e,
- 0x74, 0x4c, 0xf0, 0x04, 0x5d, 0xab, 0xd1, 0x86, 0xeb, 0x9c, 0xf9, 0x49, 0x64, 0x1a, 0x7a, 0xb7,
- 0x5d, 0x48, 0xdc, 0x30, 0x7b, 0x49, 0xe9, 0xfc, 0x94, 0x0a, 0xc5, 0xb5, 0x90, 0x47, 0xe8, 0x7b,
- 0xc4, 0xef, 0x54, 0x85, 0x2c, 0x37, 0x67, 0xfb, 0xc8, 0x85, 0x06, 0x6a, 0xb5, 0x5a, 0x0a, 0x1b,
- 0xd3, 0xbb, 0x95, 0x38, 0x6e, 0x10, 0x1e, 0x58, 0x3d, 0x5c, 0xc9, 0xec, 0x1f, 0xe3, 0x26, 0x57,
- 0x99, 0x28, 0x10, 0x06, 0xd6, 0xd2, 0x86, 0xcd, 0x9d, 0xae, 0xa4, 0xd2, 0x98, 0xc0, 0x43, 0x2b,
- 0xa1, 0x32, 0x13, 0x7d, 0x9f, 0x4b, 0x95, 0x1f, 0xe0, 0x91, 0x1d, 0xbd, 0x9a, 0xe9, 0xea, 0x83,
- 0x38, 0x20, 0x3c, 0xb6, 0xba, 0x2e, 0xa5, 0xac, 0xaa, 0x15, 0xf2, 0x03, 0x0c, 0xff, 0xa9, 0x5a,
- 0x1a, 0x73, 0xcd, 0x43, 0x7f, 0x51, 0x98, 0xed, 0xe0, 0x89, 0x75, 0x2b, 0x35, 0x4e, 0xde, 0xd2,
- 0xde, 0x02, 0x35, 0x4f, 0xb8, 0xe6, 0xa6, 0xd2, 0x3b, 0x99, 0xe0, 0x7f, 0xd3, 0x50, 0x99, 0x39,
- 0xe1, 0x9b, 0x7d, 0x9e, 0x69, 0x54, 0x51, 0x58, 0x0e, 0xc5, 0xed, 0x15, 0x6e, 0x3c, 0x7b, 0x7d,
- 0xfe, 0xed, 0xb6, 0xce, 0x17, 0x97, 0xfc, 0xb8, 0xb8, 0xe4, 0xd7, 0xc5, 0x25, 0xdf, 0xff, 0xb8,
- 0xad, 0x4f, 0x2f, 0xb6, 0x72, 0x6a, 0x86, 0x72, 0x2a, 0x64, 0x60, 0xbe, 0x01, 0x4f, 0x45, 0x50,
- 0xbc, 0x0a, 0xec, 0x41, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x79, 0xf9, 0xf5, 0xc9, 0x02,
- 0x00, 0x00,
+func (x *Metadata) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Request) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *Request) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*Metadata) ProtoMessage() {}
-func (m *Request) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Refresh != nil {
- i--
- if *m.Refresh {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
+func (x *Metadata) ProtoReflect() protoreflect.Message {
+ mi := &file_etcdserver_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x1
- i--
- dAtA[i] = 0x88
- }
- i--
- if m.Stream {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x1
- i--
- dAtA[i] = 0x80
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.Time))
- i--
- dAtA[i] = 0x78
- i--
- if m.Quorum {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x70
- i--
- if m.Sorted {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x68
- i--
- if m.Recursive {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x60
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.Since))
- i--
- dAtA[i] = 0x58
- i--
- if m.Wait {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x50
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.Expiration))
- i--
- dAtA[i] = 0x48
- if m.PrevExist != nil {
- i--
- if *m.PrevExist {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x40
- }
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.PrevIndex))
- i--
- dAtA[i] = 0x38
- i -= len(m.PrevValue)
- copy(dAtA[i:], m.PrevValue)
- i = encodeVarintEtcdserver(dAtA, i, uint64(len(m.PrevValue)))
- i--
- dAtA[i] = 0x32
- i--
- if m.Dir {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
+ return ms
}
- i--
- dAtA[i] = 0x28
- i -= len(m.Val)
- copy(dAtA[i:], m.Val)
- i = encodeVarintEtcdserver(dAtA, i, uint64(len(m.Val)))
- i--
- dAtA[i] = 0x22
- i -= len(m.Path)
- copy(dAtA[i:], m.Path)
- i = encodeVarintEtcdserver(dAtA, i, uint64(len(m.Path)))
- i--
- dAtA[i] = 0x1a
- i -= len(m.Method)
- copy(dAtA[i:], m.Method)
- i = encodeVarintEtcdserver(dAtA, i, uint64(len(m.Method)))
- i--
- dAtA[i] = 0x12
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func (m *Metadata) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *Metadata) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
+func (*Metadata) Descriptor() ([]byte, []int) {
+ return file_etcdserver_proto_rawDescGZIP(), []int{0}
}
-func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
+func (x *Metadata) GetNodeID() uint64 {
+ if x != nil && x.NodeID != nil {
+ return *x.NodeID
}
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.ClusterID))
- i--
- dAtA[i] = 0x10
- i = encodeVarintEtcdserver(dAtA, i, uint64(m.NodeID))
- i--
- dAtA[i] = 0x8
- return len(dAtA) - i, nil
+ return 0
}
-func encodeVarintEtcdserver(dAtA []byte, offset int, v uint64) int {
- offset -= sovEtcdserver(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *Request) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovEtcdserver(uint64(m.ID))
- l = len(m.Method)
- n += 1 + l + sovEtcdserver(uint64(l))
- l = len(m.Path)
- n += 1 + l + sovEtcdserver(uint64(l))
- l = len(m.Val)
- n += 1 + l + sovEtcdserver(uint64(l))
- n += 2
- l = len(m.PrevValue)
- n += 1 + l + sovEtcdserver(uint64(l))
- n += 1 + sovEtcdserver(uint64(m.PrevIndex))
- if m.PrevExist != nil {
- n += 2
- }
- n += 1 + sovEtcdserver(uint64(m.Expiration))
- n += 2
- n += 1 + sovEtcdserver(uint64(m.Since))
- n += 2
- n += 2
- n += 2
- n += 1 + sovEtcdserver(uint64(m.Time))
- n += 3
- if m.Refresh != nil {
- n += 3
+func (x *Metadata) GetClusterID() uint64 {
+ if x != nil && x.ClusterID != nil {
+ return *x.ClusterID
}
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return 0
}
-func (m *Metadata) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovEtcdserver(uint64(m.NodeID))
- n += 1 + sovEtcdserver(uint64(m.ClusterID))
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func sovEtcdserver(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozEtcdserver(x uint64) (n int) {
- return sovEtcdserver(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *Request) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Request: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthEtcdserver
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Method = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthEtcdserver
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Path = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Val", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthEtcdserver
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Val = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Dir = bool(v != 0)
- case 6:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevValue", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthEtcdserver
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PrevValue = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 7:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevIndex", wireType)
- }
- m.PrevIndex = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.PrevIndex |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 8:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevExist", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- b := bool(v != 0)
- m.PrevExist = &b
- case 9:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType)
- }
- m.Expiration = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Expiration |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 10:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Wait", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Wait = bool(v != 0)
- case 11:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Since", wireType)
- }
- m.Since = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Since |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 12:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Recursive = bool(v != 0)
- case 13:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Sorted", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Sorted = bool(v != 0)
- case 14:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Quorum", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Quorum = bool(v != 0)
- case 15:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType)
- }
- m.Time = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Time |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 16:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Stream", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Stream = bool(v != 0)
- case 17:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Refresh", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- b := bool(v != 0)
- m.Refresh = &b
- default:
- iNdEx = preIndex
- skippy, err := skipEtcdserver(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *Metadata) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Metadata: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType)
- }
- m.NodeID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.NodeID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType)
- }
- m.ClusterID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ClusterID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipEtcdserver(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthEtcdserver
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
+var File_etcdserver_proto protoreflect.FileDescriptor
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func skipEtcdserver(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowEtcdserver
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthEtcdserver
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupEtcdserver
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthEtcdserver
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+const file_etcdserver_proto_rawDesc = "" +
+ "\n" +
+ "\x10etcdserver.proto\x12\fetcdserverpb\"@\n" +
+ "\bMetadata\x12\x16\n" +
+ "\x06NodeID\x18\x01 \x01(\x04R\x06NodeID\x12\x1c\n" +
+ "\tClusterID\x18\x02 \x01(\x04R\tClusterIDB%Z#go.etcd.io/etcd/api/v3/etcdserverpb"
var (
- ErrInvalidLengthEtcdserver = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowEtcdserver = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupEtcdserver = fmt.Errorf("proto: unexpected end of group")
+ file_etcdserver_proto_rawDescOnce sync.Once
+ file_etcdserver_proto_rawDescData []byte
)
+
+func file_etcdserver_proto_rawDescGZIP() []byte {
+ file_etcdserver_proto_rawDescOnce.Do(func() {
+ file_etcdserver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_etcdserver_proto_rawDesc), len(file_etcdserver_proto_rawDesc)))
+ })
+ return file_etcdserver_proto_rawDescData
+}
+
+var file_etcdserver_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_etcdserver_proto_goTypes = []any{
+ (*Metadata)(nil), // 0: etcdserverpb.Metadata
+}
+var file_etcdserver_proto_depIdxs = []int32{
+ 0, // [0:0] is the sub-list for method output_type
+ 0, // [0:0] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_etcdserver_proto_init() }
+func file_etcdserver_proto_init() {
+ if File_etcdserver_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_etcdserver_proto_rawDesc), len(file_etcdserver_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_etcdserver_proto_goTypes,
+ DependencyIndexes: file_etcdserver_proto_depIdxs,
+ MessageInfos: file_etcdserver_proto_msgTypes,
+ }.Build()
+ File_etcdserver_proto = out.File
+ file_etcdserver_proto_goTypes = nil
+ file_etcdserver_proto_depIdxs = nil
+}
diff --git a/api/etcdserverpb/etcdserver.proto b/api/etcdserverpb/etcdserver.proto
index ff639b9c96c7..e824f71e5bbf 100644
--- a/api/etcdserverpb/etcdserver.proto
+++ b/api/etcdserverpb/etcdserver.proto
@@ -1,36 +1,9 @@
syntax = "proto2";
package etcdserverpb;
-import "gogoproto/gogo.proto";
-
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-
-message Request {
- optional uint64 ID = 1 [(gogoproto.nullable) = false];
- optional string Method = 2 [(gogoproto.nullable) = false];
- optional string Path = 3 [(gogoproto.nullable) = false];
- optional string Val = 4 [(gogoproto.nullable) = false];
- optional bool Dir = 5 [(gogoproto.nullable) = false];
- optional string PrevValue = 6 [(gogoproto.nullable) = false];
- optional uint64 PrevIndex = 7 [(gogoproto.nullable) = false];
- optional bool PrevExist = 8 [(gogoproto.nullable) = true];
- optional int64 Expiration = 9 [(gogoproto.nullable) = false];
- optional bool Wait = 10 [(gogoproto.nullable) = false];
- optional uint64 Since = 11 [(gogoproto.nullable) = false];
- optional bool Recursive = 12 [(gogoproto.nullable) = false];
- optional bool Sorted = 13 [(gogoproto.nullable) = false];
- optional bool Quorum = 14 [(gogoproto.nullable) = false];
- optional int64 Time = 15 [(gogoproto.nullable) = false];
- optional bool Stream = 16 [(gogoproto.nullable) = false];
- optional bool Refresh = 17 [(gogoproto.nullable) = true];
-}
-
message Metadata {
- optional uint64 NodeID = 1 [(gogoproto.nullable) = false];
- optional uint64 ClusterID = 2 [(gogoproto.nullable) = false];
+ optional uint64 NodeID = 1;
+ optional uint64 ClusterID = 2;
}
diff --git a/api/etcdserverpb/gw/rpc.pb.gw.go b/api/etcdserverpb/gw/rpc.pb.gw.go
index 6ad1e9d9c653..8bad7f150c90 100644
--- a/api/etcdserverpb/gw/rpc.pb.gw.go
+++ b/api/etcdserverpb/gw/rpc.pb.gw.go
@@ -9,8 +9,6 @@ It translates gRPC into RESTful JSON APIs.
package gw
import (
- protov1 "github.com/golang/protobuf/proto"
-
"context"
"errors"
"go.etcd.io/etcd/api/v3/etcdserverpb"
@@ -43,11 +41,14 @@ func request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler, client
protoReq etcdserverpb.RangeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Range(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -55,11 +56,11 @@ func local_request_KV_Range_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.RangeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Range(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -67,11 +68,14 @@ func request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, client e
protoReq etcdserverpb.PutRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Put(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -79,11 +83,11 @@ func local_request_KV_Put_0(ctx context.Context, marshaler runtime.Marshaler, se
protoReq etcdserverpb.PutRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Put(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -91,11 +95,14 @@ func request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.DeleteRangeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.DeleteRange(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -103,11 +110,11 @@ func local_request_KV_DeleteRange_0(ctx context.Context, marshaler runtime.Marsh
protoReq etcdserverpb.DeleteRangeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.DeleteRange(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -115,11 +122,14 @@ func request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, client e
protoReq etcdserverpb.TxnRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Txn(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -127,11 +137,11 @@ func local_request_KV_Txn_0(ctx context.Context, marshaler runtime.Marshaler, se
protoReq etcdserverpb.TxnRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Txn(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.KVClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -139,11 +149,14 @@ func request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler, clie
protoReq etcdserverpb.CompactionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Compact(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.KVServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -151,11 +164,11 @@ func local_request_KV_Compact_0(ctx context.Context, marshaler runtime.Marshaler
protoReq etcdserverpb.CompactionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Compact(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Watch_Watch_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.WatchClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Watch_WatchClient, runtime.ServerMetadata, error) {
@@ -168,7 +181,7 @@ func request_Watch_Watch_0(ctx context.Context, marshaler runtime.Marshaler, cli
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq etcdserverpb.WatchRequest
- err := dec.Decode(protov1.MessageV2(&protoReq))
+ err := dec.Decode(&protoReq)
if errors.Is(err, io.EOF) {
return err
}
@@ -206,11 +219,14 @@ func request_Lease_LeaseGrant_0(ctx context.Context, marshaler runtime.Marshaler
protoReq etcdserverpb.LeaseGrantRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseGrant(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseGrant_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -218,11 +234,11 @@ func local_request_Lease_LeaseGrant_0(ctx context.Context, marshaler runtime.Mar
protoReq etcdserverpb.LeaseGrantRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseGrant(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -230,11 +246,14 @@ func request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseRevoke(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -242,11 +261,11 @@ func local_request_Lease_LeaseRevoke_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseRevoke(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -254,11 +273,14 @@ func request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseRevoke(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -266,11 +288,11 @@ func local_request_Lease_LeaseRevoke_1(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.LeaseRevokeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseRevoke(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseKeepAlive_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Lease_LeaseKeepAliveClient, runtime.ServerMetadata, error) {
@@ -283,7 +305,7 @@ func request_Lease_LeaseKeepAlive_0(ctx context.Context, marshaler runtime.Marsh
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq etcdserverpb.LeaseKeepAliveRequest
- err := dec.Decode(protov1.MessageV2(&protoReq))
+ err := dec.Decode(&protoReq)
if errors.Is(err, io.EOF) {
return err
}
@@ -321,11 +343,14 @@ func request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseTimeToLive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -333,11 +358,11 @@ func local_request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtim
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseTimeToLive(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -345,11 +370,14 @@ func request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseTimeToLive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -357,11 +385,11 @@ func local_request_Lease_LeaseTimeToLive_1(ctx context.Context, marshaler runtim
protoReq etcdserverpb.LeaseTimeToLiveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseTimeToLive(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -369,11 +397,14 @@ func request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseLeases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -381,11 +412,11 @@ func local_request_Lease_LeaseLeases_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseLeases(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -393,11 +424,14 @@ func request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.LeaseLeases(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.LeaseServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -405,11 +439,11 @@ func local_request_Lease_LeaseLeases_1(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.LeaseLeasesRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.LeaseLeases(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -417,11 +451,14 @@ func request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.MemberAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MemberAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -429,11 +466,11 @@ func local_request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.MemberAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberAdd(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -441,11 +478,14 @@ func request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime.Marsh
protoReq etcdserverpb.MemberRemoveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MemberRemove(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -453,11 +493,11 @@ func local_request_Cluster_MemberRemove_0(ctx context.Context, marshaler runtime
protoReq etcdserverpb.MemberRemoveRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberRemove(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -465,11 +505,14 @@ func request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime.Marsh
protoReq etcdserverpb.MemberUpdateRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MemberUpdate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -477,11 +520,11 @@ func local_request_Cluster_MemberUpdate_0(ctx context.Context, marshaler runtime
protoReq etcdserverpb.MemberUpdateRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberUpdate(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -489,11 +532,14 @@ func request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.MemberListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MemberList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -501,11 +547,11 @@ func local_request_Cluster_MemberList_0(ctx context.Context, marshaler runtime.M
protoReq etcdserverpb.MemberListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberList(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -513,11 +559,14 @@ func request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.MemberPromoteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MemberPromote(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.ClusterServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -525,11 +574,11 @@ func local_request_Cluster_MemberPromote_0(ctx context.Context, marshaler runtim
protoReq etcdserverpb.MemberPromoteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MemberPromote(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -537,11 +586,14 @@ func request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.AlarmRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Alarm(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -549,11 +601,11 @@ func local_request_Maintenance_Alarm_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.AlarmRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Alarm(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Status_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -561,11 +613,14 @@ func request_Maintenance_Status_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.StatusRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Status(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_Status_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -573,11 +628,11 @@ func local_request_Maintenance_Status_0(ctx context.Context, marshaler runtime.M
protoReq etcdserverpb.StatusRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Status(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Defragment_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -585,11 +640,14 @@ func request_Maintenance_Defragment_0(ctx context.Context, marshaler runtime.Mar
protoReq etcdserverpb.DefragmentRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Defragment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_Defragment_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -597,11 +655,11 @@ func local_request_Maintenance_Defragment_0(ctx context.Context, marshaler runti
protoReq etcdserverpb.DefragmentRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Defragment(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -609,11 +667,14 @@ func request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler
protoReq etcdserverpb.HashRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Hash(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -621,11 +682,11 @@ func local_request_Maintenance_Hash_0(ctx context.Context, marshaler runtime.Mar
protoReq etcdserverpb.HashRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Hash(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -633,11 +694,14 @@ func request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.HashKVRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.HashKV(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -645,11 +709,11 @@ func local_request_Maintenance_HashKV_0(ctx context.Context, marshaler runtime.M
protoReq etcdserverpb.HashKVRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.HashKV(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Snapshot_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (etcdserverpb.Maintenance_SnapshotClient, runtime.ServerMetadata, error) {
@@ -657,9 +721,12 @@ func request_Maintenance_Snapshot_0(ctx context.Context, marshaler runtime.Marsh
protoReq etcdserverpb.SnapshotRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
stream, err := client.Snapshot(ctx, &protoReq)
if err != nil {
return nil, metadata, err
@@ -677,11 +744,14 @@ func request_Maintenance_MoveLeader_0(ctx context.Context, marshaler runtime.Mar
protoReq etcdserverpb.MoveLeaderRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.MoveLeader(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_MoveLeader_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -689,11 +759,11 @@ func local_request_Maintenance_MoveLeader_0(ctx context.Context, marshaler runti
protoReq etcdserverpb.MoveLeaderRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.MoveLeader(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.MaintenanceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -701,11 +771,14 @@ func request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.DowngradeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Downgrade(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.MaintenanceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -713,11 +786,11 @@ func local_request_Maintenance_Downgrade_0(ctx context.Context, marshaler runtim
protoReq etcdserverpb.DowngradeRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Downgrade(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -725,11 +798,14 @@ func request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.AuthEnableRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.AuthEnable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -737,11 +813,11 @@ func local_request_Auth_AuthEnable_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.AuthEnableRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthEnable(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -749,11 +825,14 @@ func request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Marshaler
protoReq etcdserverpb.AuthDisableRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.AuthDisable(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -761,11 +840,11 @@ func local_request_Auth_AuthDisable_0(ctx context.Context, marshaler runtime.Mar
protoReq etcdserverpb.AuthDisableRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthDisable(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -773,11 +852,14 @@ func request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.AuthStatusRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.AuthStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -785,11 +867,11 @@ func local_request_Auth_AuthStatus_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.AuthStatusRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.AuthStatus(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -797,11 +879,14 @@ func request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Marshale
protoReq etcdserverpb.AuthenticateRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Authenticate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -809,11 +894,11 @@ func local_request_Auth_Authenticate_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.AuthenticateRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Authenticate(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -821,11 +906,14 @@ func request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshaler, cl
protoReq etcdserverpb.AuthUserAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -833,11 +921,11 @@ func local_request_Auth_UserAdd_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.AuthUserAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserAdd(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -845,11 +933,14 @@ func request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshaler, cl
protoReq etcdserverpb.AuthUserGetRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserGet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -857,11 +948,11 @@ func local_request_Auth_UserGet_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.AuthUserGetRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserGet(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -869,11 +960,14 @@ func request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marshaler, c
protoReq etcdserverpb.AuthUserListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -881,11 +975,11 @@ func local_request_Auth_UserList_0(ctx context.Context, marshaler runtime.Marsha
protoReq etcdserverpb.AuthUserListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserList(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -893,11 +987,14 @@ func request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.AuthUserDeleteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -905,11 +1002,11 @@ func local_request_Auth_UserDelete_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.AuthUserDeleteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserDelete(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserChangePassword_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -917,11 +1014,14 @@ func request_Auth_UserChangePassword_0(ctx context.Context, marshaler runtime.Ma
protoReq etcdserverpb.AuthUserChangePasswordRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserChangePassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserChangePassword_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -929,11 +1029,11 @@ func local_request_Auth_UserChangePassword_0(ctx context.Context, marshaler runt
protoReq etcdserverpb.AuthUserChangePasswordRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserChangePassword(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -941,11 +1041,14 @@ func request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.AuthUserGrantRoleRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserGrantRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -953,11 +1056,11 @@ func local_request_Auth_UserGrantRole_0(ctx context.Context, marshaler runtime.M
protoReq etcdserverpb.AuthUserGrantRoleRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserGrantRole(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -965,11 +1068,14 @@ func request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.Marsha
protoReq etcdserverpb.AuthUserRevokeRoleRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.UserRevokeRole(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -977,11 +1083,11 @@ func local_request_Auth_UserRevokeRole_0(ctx context.Context, marshaler runtime.
protoReq etcdserverpb.AuthUserRevokeRoleRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UserRevokeRole(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -989,11 +1095,14 @@ func request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshaler, cl
protoReq etcdserverpb.AuthRoleAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleAdd(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1001,11 +1110,11 @@ func local_request_Auth_RoleAdd_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.AuthRoleAddRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleAdd(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1013,11 +1122,14 @@ func request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshaler, cl
protoReq etcdserverpb.AuthRoleGetRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleGet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1025,11 +1137,11 @@ func local_request_Auth_RoleGet_0(ctx context.Context, marshaler runtime.Marshal
protoReq etcdserverpb.AuthRoleGetRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleGet(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1037,11 +1149,14 @@ func request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marshaler, c
protoReq etcdserverpb.AuthRoleListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1049,11 +1164,11 @@ func local_request_Auth_RoleList_0(ctx context.Context, marshaler runtime.Marsha
protoReq etcdserverpb.AuthRoleListRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleList(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1061,11 +1176,14 @@ func request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq etcdserverpb.AuthRoleDeleteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleDelete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1073,11 +1191,11 @@ func local_request_Auth_RoleDelete_0(ctx context.Context, marshaler runtime.Mars
protoReq etcdserverpb.AuthRoleDeleteRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleDelete(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1085,11 +1203,14 @@ func request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler runtime.M
protoReq etcdserverpb.AuthRoleGrantPermissionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleGrantPermission(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1097,11 +1218,11 @@ func local_request_Auth_RoleGrantPermission_0(ctx context.Context, marshaler run
protoReq etcdserverpb.AuthRoleGrantPermissionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleGrantPermission(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler runtime.Marshaler, client etcdserverpb.AuthClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1109,11 +1230,14 @@ func request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler runtime.
protoReq etcdserverpb.AuthRoleRevokePermissionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.RoleRevokePermission(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler runtime.Marshaler, server etcdserverpb.AuthServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -1121,11 +1245,11 @@ func local_request_Auth_RoleRevokePermission_0(ctx context.Context, marshaler ru
protoReq etcdserverpb.AuthRoleRevokePermissionRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RoleRevokePermission(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
// etcdserverpb.RegisterKVHandlerServer registers the http handlers for service KV to "mux".
@@ -2219,10 +2343,7 @@ func RegisterWatchHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_Watch_Watch_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
- m1, err := resp.Recv()
- return protov1.MessageV2(m1), err
- }, mux.GetForwardResponseOptions()...)
+ forward_Watch_Watch_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
})
return nil
}
@@ -2337,10 +2458,7 @@ func RegisterLeaseHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_Lease_LeaseKeepAlive_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
- m1, err := resp.Recv()
- return protov1.MessageV2(m1), err
- }, mux.GetForwardResponseOptions()...)
+ forward_Lease_LeaseKeepAlive_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Lease_LeaseTimeToLive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
@@ -2711,10 +2829,7 @@ func RegisterMaintenanceHandlerClient(ctx context.Context, mux *runtime.ServeMux
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_Maintenance_Snapshot_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
- m1, err := resp.Recv()
- return protov1.MessageV2(m1), err
- }, mux.GetForwardResponseOptions()...)
+ forward_Maintenance_Snapshot_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_Maintenance_MoveLeader_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
diff --git a/api/etcdserverpb/raft_internal.pb.go b/api/etcdserverpb/raft_internal.pb.go
index e1cd9ebecd69..812183a768b2 100644
--- a/api/etcdserverpb/raft_internal.pb.go
+++ b/api/etcdserverpb/raft_internal.pb.go
@@ -1,81 +1,97 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: raft_internal.proto
package etcdserverpb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
membershippb "go.etcd.io/etcd/api/v3/membershippb"
_ "go.etcd.io/etcd/api/v3/versionpb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type RequestHeader struct {
- ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// username is a username that is associated with an auth token of gRPC connection
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
// auth_revision is a revision number of auth.authStore. It is not related to mvcc
- AuthRevision uint64 `protobuf:"varint,3,opt,name=auth_revision,json=authRevision,proto3" json:"auth_revision,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ AuthRevision uint64 `protobuf:"varint,3,opt,name=auth_revision,json=authRevision,proto3" json:"auth_revision,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *RequestHeader) Reset() { *m = RequestHeader{} }
-func (m *RequestHeader) String() string { return proto.CompactTextString(m) }
-func (*RequestHeader) ProtoMessage() {}
-func (*RequestHeader) Descriptor() ([]byte, []int) {
- return fileDescriptor_b4c9a9be0cfca103, []int{0}
+func (x *RequestHeader) Reset() {
+ *x = RequestHeader{}
+ mi := &file_raft_internal_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *RequestHeader) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
+
+func (x *RequestHeader) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *RequestHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_RequestHeader.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+
+func (*RequestHeader) ProtoMessage() {}
+
+func (x *RequestHeader) ProtoReflect() protoreflect.Message {
+ mi := &file_raft_internal_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *RequestHeader) XXX_Merge(src proto.Message) {
- xxx_messageInfo_RequestHeader.Merge(m, src)
+
+// Deprecated: Use RequestHeader.ProtoReflect.Descriptor instead.
+func (*RequestHeader) Descriptor() ([]byte, []int) {
+ return file_raft_internal_proto_rawDescGZIP(), []int{0}
}
-func (m *RequestHeader) XXX_Size() int {
- return m.Size()
+
+func (x *RequestHeader) GetID() uint64 {
+ if x != nil {
+ return x.ID
+ }
+ return 0
}
-func (m *RequestHeader) XXX_DiscardUnknown() {
- xxx_messageInfo_RequestHeader.DiscardUnknown(m)
+
+func (x *RequestHeader) GetUsername() string {
+ if x != nil {
+ return x.Username
+ }
+ return ""
}
-var xxx_messageInfo_RequestHeader proto.InternalMessageInfo
+func (x *RequestHeader) GetAuthRevision() uint64 {
+ if x != nil {
+ return x.AuthRevision
+ }
+ return 0
+}
// An InternalRaftRequest is the union of all requests which can be
// sent via raft.
type InternalRaftRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
Header *RequestHeader `protobuf:"bytes,100,opt,name=header,proto3" json:"header,omitempty"`
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- V2 *Request `protobuf:"bytes,2,opt,name=v2,proto3" json:"v2,omitempty"`
Range *RangeRequest `protobuf:"bytes,3,opt,name=range,proto3" json:"range,omitempty"`
Put *PutRequest `protobuf:"bytes,4,opt,name=put,proto3" json:"put,omitempty"`
DeleteRange *DeleteRangeRequest `protobuf:"bytes,5,opt,name=delete_range,json=deleteRange,proto3" json:"delete_range,omitempty"`
@@ -106,2632 +122,529 @@ type InternalRaftRequest struct {
ClusterMemberAttrSet *membershippb.ClusterMemberAttrSetRequest `protobuf:"bytes,1301,opt,name=cluster_member_attr_set,json=clusterMemberAttrSet,proto3" json:"cluster_member_attr_set,omitempty"`
DowngradeInfoSet *membershippb.DowngradeInfoSetRequest `protobuf:"bytes,1302,opt,name=downgrade_info_set,json=downgradeInfoSet,proto3" json:"downgrade_info_set,omitempty"`
DowngradeVersionTest *DowngradeVersionTestRequest `protobuf:"bytes,9900,opt,name=downgrade_version_test,json=downgradeVersionTest,proto3" json:"downgrade_version_test,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} }
-func (m *InternalRaftRequest) String() string { return proto.CompactTextString(m) }
-func (*InternalRaftRequest) ProtoMessage() {}
-func (*InternalRaftRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_b4c9a9be0cfca103, []int{1}
+func (x *InternalRaftRequest) Reset() {
+ *x = InternalRaftRequest{}
+ mi := &file_raft_internal_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *InternalRaftRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
+
+func (x *InternalRaftRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *InternalRaftRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_InternalRaftRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+
+func (*InternalRaftRequest) ProtoMessage() {}
+
+func (x *InternalRaftRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_raft_internal_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *InternalRaftRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_InternalRaftRequest.Merge(m, src)
-}
-func (m *InternalRaftRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *InternalRaftRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_InternalRaftRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_InternalRaftRequest proto.InternalMessageInfo
-type EmptyResponse struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use InternalRaftRequest.ProtoReflect.Descriptor instead.
+func (*InternalRaftRequest) Descriptor() ([]byte, []int) {
+ return file_raft_internal_proto_rawDescGZIP(), []int{1}
}
-func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
-func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
-func (*EmptyResponse) ProtoMessage() {}
-func (*EmptyResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_b4c9a9be0cfca103, []int{2}
-}
-func (m *EmptyResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *InternalRaftRequest) GetHeader() *RequestHeader {
+ if x != nil {
+ return x.Header
}
-}
-func (m *EmptyResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_EmptyResponse.Merge(m, src)
-}
-func (m *EmptyResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *EmptyResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_EmptyResponse.DiscardUnknown(m)
+ return nil
}
-var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo
-
-// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
-// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
-// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
-type InternalAuthenticateRequest struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
- // simple_token is generated in API layer (etcdserver/v3_server.go)
- SimpleToken string `protobuf:"bytes,3,opt,name=simple_token,json=simpleToken,proto3" json:"simple_token,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *InternalRaftRequest) GetID() uint64 {
+ if x != nil {
+ return x.ID
+ }
+ return 0
}
-func (m *InternalAuthenticateRequest) Reset() { *m = InternalAuthenticateRequest{} }
-func (m *InternalAuthenticateRequest) String() string { return proto.CompactTextString(m) }
-func (*InternalAuthenticateRequest) ProtoMessage() {}
-func (*InternalAuthenticateRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_b4c9a9be0cfca103, []int{3}
-}
-func (m *InternalAuthenticateRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *InternalAuthenticateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_InternalAuthenticateRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *InternalRaftRequest) GetRange() *RangeRequest {
+ if x != nil {
+ return x.Range
}
+ return nil
}
-func (m *InternalAuthenticateRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_InternalAuthenticateRequest.Merge(m, src)
-}
-func (m *InternalAuthenticateRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *InternalAuthenticateRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_InternalAuthenticateRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_InternalAuthenticateRequest proto.InternalMessageInfo
-func init() {
- proto.RegisterType((*RequestHeader)(nil), "etcdserverpb.RequestHeader")
- proto.RegisterType((*InternalRaftRequest)(nil), "etcdserverpb.InternalRaftRequest")
- proto.RegisterType((*EmptyResponse)(nil), "etcdserverpb.EmptyResponse")
- proto.RegisterType((*InternalAuthenticateRequest)(nil), "etcdserverpb.InternalAuthenticateRequest")
+func (x *InternalRaftRequest) GetPut() *PutRequest {
+ if x != nil {
+ return x.Put
+ }
+ return nil
}
-func init() { proto.RegisterFile("raft_internal.proto", fileDescriptor_b4c9a9be0cfca103) }
-
-var fileDescriptor_b4c9a9be0cfca103 = []byte{
- // 1101 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x56, 0xcb, 0x72, 0x1b, 0x45,
- 0x14, 0x8d, 0x6c, 0xc7, 0xb6, 0x5a, 0xb6, 0xe3, 0xb4, 0x9d, 0xa4, 0xb1, 0xab, 0x8c, 0xe3, 0x90,
- 0x60, 0x20, 0xc8, 0xc1, 0x06, 0xaa, 0x60, 0x03, 0x8a, 0xe5, 0x72, 0x4c, 0x25, 0x29, 0xd7, 0xc4,
- 0x50, 0x29, 0x28, 0x6a, 0x68, 0xcd, 0x5c, 0x4b, 0x13, 0x8f, 0x66, 0x86, 0xee, 0x96, 0xe2, 0x6c,
- 0x59, 0xb2, 0x06, 0x8a, 0x8f, 0x60, 0xc1, 0x2b, 0xff, 0x90, 0x05, 0x8f, 0x00, 0x3f, 0x00, 0x66,
- 0xc3, 0x1e, 0xd8, 0xa7, 0xfa, 0x31, 0x2f, 0xa9, 0xe5, 0xdd, 0xe8, 0xde, 0x73, 0xcf, 0x39, 0xdd,
- 0x7d, 0xbb, 0x75, 0xd1, 0x02, 0xa3, 0x87, 0xc2, 0x0d, 0x22, 0x01, 0x2c, 0xa2, 0x61, 0x3d, 0x61,
- 0xb1, 0x88, 0xf1, 0x0c, 0x08, 0xcf, 0xe7, 0xc0, 0xfa, 0xc0, 0x92, 0xd6, 0xd2, 0x62, 0x3b, 0x6e,
- 0xc7, 0x2a, 0xb1, 0x21, 0xbf, 0x34, 0x66, 0x69, 0x3e, 0xc7, 0x98, 0x48, 0x95, 0x25, 0x9e, 0xf9,
- 0x5c, 0x95, 0xc9, 0x0d, 0x9a, 0x04, 0x1b, 0x7d, 0x60, 0x3c, 0x88, 0xa3, 0xa4, 0x95, 0x7e, 0x19,
- 0xc4, 0xb5, 0x0c, 0xd1, 0x85, 0x6e, 0x0b, 0x18, 0xef, 0x04, 0x49, 0xd2, 0x2a, 0xfc, 0xd0, 0xb8,
- 0x35, 0x86, 0x66, 0x1d, 0xf8, 0xb4, 0x07, 0x5c, 0xdc, 0x02, 0xea, 0x03, 0xc3, 0x73, 0x68, 0x6c,
- 0xaf, 0x49, 0x2a, 0xab, 0x95, 0xf5, 0x09, 0x67, 0x6c, 0xaf, 0x89, 0x97, 0xd0, 0x74, 0x8f, 0x4b,
- 0xf3, 0x5d, 0x20, 0x63, 0xab, 0x95, 0xf5, 0xaa, 0x93, 0xfd, 0xc6, 0xd7, 0xd1, 0x2c, 0xed, 0x89,
- 0x8e, 0xcb, 0xa0, 0x1f, 0x48, 0x6d, 0x32, 0x2e, 0xcb, 0x6e, 0x4e, 0x7d, 0xfe, 0x98, 0x8c, 0x6f,
- 0xd5, 0x5f, 0x73, 0x66, 0x64, 0xd6, 0x31, 0xc9, 0xb7, 0xa7, 0x3e, 0x53, 0xe1, 0x1b, 0x6b, 0x8f,
- 0x17, 0xd0, 0xc2, 0x9e, 0xd9, 0x11, 0x87, 0x1e, 0x0a, 0x63, 0x00, 0x6f, 0xa1, 0xc9, 0x8e, 0x32,
- 0x41, 0xfc, 0xd5, 0xca, 0x7a, 0x6d, 0x73, 0xb9, 0x5e, 0xdc, 0xa7, 0x7a, 0xc9, 0xa7, 0x63, 0xa0,
- 0x43, 0x7e, 0xaf, 0xa2, 0xb1, 0xfe, 0xa6, 0x72, 0x5a, 0xdb, 0xbc, 0x60, 0x25, 0x70, 0xc6, 0xfa,
- 0x9b, 0xf8, 0x06, 0x3a, 0xcb, 0x68, 0xd4, 0x06, 0x65, 0xb9, 0xb6, 0xb9, 0x34, 0x80, 0x94, 0xa9,
- 0x14, 0xae, 0x81, 0xf8, 0x65, 0x34, 0x9e, 0xf4, 0x04, 0x99, 0x50, 0x78, 0x52, 0xc6, 0xef, 0xf7,
- 0xd2, 0x45, 0x38, 0x12, 0x84, 0xb7, 0xd1, 0x8c, 0x0f, 0x21, 0x08, 0x70, 0xb5, 0xc8, 0x59, 0x55,
- 0xb4, 0x5a, 0x2e, 0x6a, 0x2a, 0x44, 0x49, 0xaa, 0xe6, 0xe7, 0x31, 0x29, 0x28, 0x8e, 0x23, 0x32,
- 0x69, 0x13, 0x3c, 0x38, 0x8e, 0x32, 0x41, 0x71, 0x1c, 0xe1, 0x77, 0x10, 0xf2, 0xe2, 0x6e, 0x42,
- 0x3d, 0x21, 0x8f, 0x61, 0x4a, 0x95, 0x3c, 0x5f, 0x2e, 0xd9, 0xce, 0xf2, 0x69, 0x65, 0xa1, 0x04,
- 0xbf, 0x8b, 0x6a, 0x21, 0x50, 0x0e, 0x6e, 0x9b, 0xd1, 0x48, 0x90, 0x69, 0x1b, 0xc3, 0x6d, 0x09,
- 0xd8, 0x95, 0xf9, 0x8c, 0x21, 0xcc, 0x42, 0x72, 0xcd, 0x9a, 0x81, 0x41, 0x3f, 0x3e, 0x02, 0x52,
- 0xb5, 0xad, 0x59, 0x51, 0x38, 0x0a, 0x90, 0xad, 0x39, 0xcc, 0x63, 0xf2, 0x58, 0x68, 0x48, 0x59,
- 0x97, 0x20, 0xdb, 0xb1, 0x34, 0x64, 0x2a, 0x3b, 0x16, 0x05, 0xc4, 0xf7, 0xd1, 0xbc, 0x96, 0xf5,
- 0x3a, 0xe0, 0x1d, 0x25, 0x71, 0x10, 0x09, 0x52, 0x53, 0xc5, 0x2f, 0x58, 0xa4, 0xb7, 0x33, 0x90,
- 0xa1, 0x49, 0x9b, 0xf5, 0x75, 0xe7, 0x5c, 0x58, 0x06, 0xe0, 0x06, 0xaa, 0xa9, 0xee, 0x86, 0x88,
- 0xb6, 0x42, 0x20, 0xff, 0x58, 0x77, 0xb5, 0xd1, 0x13, 0x9d, 0x1d, 0x05, 0xc8, 0xf6, 0x84, 0x66,
- 0x21, 0xdc, 0x44, 0xea, 0x0a, 0xb8, 0x7e, 0xc0, 0x15, 0xc7, 0xbf, 0x53, 0xb6, 0x4d, 0x91, 0x1c,
- 0x4d, 0x8d, 0xc8, 0x36, 0x85, 0xe6, 0x31, 0xfc, 0x9e, 0x31, 0xc2, 0x05, 0x15, 0x3d, 0x4e, 0xfe,
- 0x1f, 0x69, 0xe4, 0x9e, 0x02, 0x0c, 0xac, 0xec, 0x0d, 0xed, 0x48, 0xe7, 0xf0, 0x5d, 0xed, 0x08,
- 0x22, 0x11, 0x78, 0x54, 0x00, 0xf9, 0x4f, 0x93, 0xbd, 0x54, 0x26, 0x4b, 0x6f, 0x67, 0xa3, 0x00,
- 0x4d, 0xad, 0x95, 0xea, 0xf1, 0x8e, 0x79, 0x02, 0xe4, 0x9b, 0xe0, 0x52, 0xdf, 0x27, 0x3f, 0x4d,
- 0x8f, 0x5a, 0xe2, 0xfb, 0x1c, 0x58, 0xc3, 0xf7, 0x4b, 0x4b, 0x34, 0x31, 0x7c, 0x17, 0xcd, 0xe7,
- 0x34, 0xfa, 0x12, 0x90, 0x9f, 0x35, 0xd3, 0x15, 0x3b, 0x93, 0xb9, 0x3d, 0x86, 0x6c, 0x8e, 0x96,
- 0xc2, 0x65, 0x5b, 0x6d, 0x10, 0xe4, 0x97, 0x53, 0x6d, 0xed, 0x82, 0x18, 0xb2, 0xb5, 0x0b, 0x02,
- 0xb7, 0xd1, 0x73, 0x39, 0x8d, 0xd7, 0x91, 0xd7, 0xd2, 0x4d, 0x28, 0xe7, 0x0f, 0x63, 0xe6, 0x93,
- 0x5f, 0x35, 0xe5, 0x2b, 0x76, 0xca, 0x6d, 0x85, 0xde, 0x37, 0xe0, 0x94, 0xfd, 0x22, 0xb5, 0xa6,
- 0xf1, 0x7d, 0xb4, 0x58, 0xf0, 0x2b, 0xef, 0x93, 0xcb, 0xe2, 0x10, 0xc8, 0x53, 0xad, 0x71, 0x6d,
- 0x84, 0x6d, 0x75, 0x17, 0xe3, 0xbc, 0x6d, 0xce, 0xd3, 0xc1, 0x0c, 0xfe, 0x08, 0x5d, 0xc8, 0x99,
- 0xf5, 0xd5, 0xd4, 0xd4, 0xbf, 0x69, 0xea, 0x17, 0xed, 0xd4, 0xe6, 0x8e, 0x16, 0xb8, 0x31, 0x1d,
- 0x4a, 0xe1, 0x5b, 0x68, 0x2e, 0x27, 0x0f, 0x03, 0x2e, 0xc8, 0xef, 0x9a, 0xf5, 0xb2, 0x9d, 0xf5,
- 0x76, 0xc0, 0x45, 0xa9, 0x8f, 0xd2, 0x60, 0xc6, 0x24, 0xad, 0x69, 0xa6, 0x3f, 0x46, 0x32, 0x49,
- 0xe9, 0x21, 0xa6, 0x34, 0x98, 0x1d, 0xbd, 0x62, 0x92, 0x1d, 0xf9, 0x6d, 0x75, 0xd4, 0xd1, 0xcb,
- 0x9a, 0xc1, 0x8e, 0x34, 0xb1, 0xac, 0x23, 0x15, 0x8d, 0xe9, 0xc8, 0xef, 0xaa, 0xa3, 0x3a, 0x52,
- 0x56, 0x59, 0x3a, 0x32, 0x0f, 0x97, 0x6d, 0xc9, 0x8e, 0xfc, 0xfe, 0x54, 0x5b, 0x83, 0x1d, 0x69,
- 0x62, 0xf8, 0x01, 0x5a, 0x2a, 0xd0, 0xa8, 0x46, 0x49, 0x80, 0x75, 0x03, 0xae, 0xfe, 0x7f, 0x7f,
- 0xd0, 0x9c, 0xd7, 0x47, 0x70, 0x4a, 0xf8, 0x7e, 0x86, 0x4e, 0xf9, 0x2f, 0x51, 0x7b, 0x1e, 0x77,
- 0xd1, 0x72, 0xae, 0x65, 0x5a, 0xa7, 0x20, 0xf6, 0xa3, 0x16, 0x7b, 0xd5, 0x2e, 0xa6, 0xbb, 0x64,
- 0x58, 0x8d, 0xd0, 0x11, 0x00, 0xfc, 0x09, 0x5a, 0xf0, 0xc2, 0x1e, 0x17, 0xc0, 0x5c, 0x33, 0xcb,
- 0xb8, 0x1c, 0x04, 0xf9, 0x02, 0x99, 0x2b, 0x50, 0x1c, 0x64, 0xea, 0xdb, 0x1a, 0xf9, 0x81, 0x06,
- 0xde, 0x03, 0x31, 0xf4, 0xea, 0x9d, 0xf7, 0x06, 0x21, 0xf8, 0x01, 0xba, 0x94, 0x2a, 0x68, 0x32,
- 0x97, 0x0a, 0xc1, 0x94, 0xca, 0x97, 0xc8, 0xbc, 0x83, 0x36, 0x95, 0x3b, 0x2a, 0xd6, 0x10, 0x82,
- 0xd9, 0x84, 0x16, 0x3d, 0x0b, 0x0a, 0x7f, 0x8c, 0xb0, 0x1f, 0x3f, 0x8c, 0xda, 0x8c, 0xfa, 0xe0,
- 0x06, 0xd1, 0x61, 0xac, 0x64, 0xbe, 0xd2, 0x32, 0x57, 0xcb, 0x32, 0xcd, 0x14, 0xb8, 0x17, 0x1d,
- 0xc6, 0x36, 0x89, 0x79, 0x7f, 0x00, 0x81, 0x03, 0x74, 0x31, 0xa7, 0x4f, 0xb7, 0x4b, 0x00, 0x17,
- 0xe4, 0x9b, 0x3b, 0xb6, 0x17, 0x3d, 0x93, 0x30, 0xdb, 0x71, 0x00, 0x7c, 0x50, 0xe6, 0x4d, 0x67,
- 0xd1, 0xb7, 0xa0, 0xf2, 0xb9, 0xed, 0x1c, 0x9a, 0xdd, 0xe9, 0x26, 0xe2, 0x91, 0x03, 0x3c, 0x89,
- 0x23, 0x0e, 0x6b, 0x8f, 0xd0, 0xf2, 0x29, 0xff, 0x14, 0x18, 0xa3, 0x09, 0x35, 0x36, 0x56, 0xd4,
- 0xd8, 0xa8, 0xbe, 0xe5, 0x38, 0x99, 0x3d, 0xa0, 0x66, 0x9c, 0x4c, 0x7f, 0xe3, 0xcb, 0x68, 0x86,
- 0x07, 0xdd, 0x24, 0x04, 0x57, 0xc4, 0x47, 0xa0, 0xa7, 0xc9, 0xaa, 0x53, 0xd3, 0xb1, 0x03, 0x19,
- 0xca, 0xbc, 0xdc, 0x7c, 0xeb, 0xc9, 0x5f, 0x2b, 0x67, 0x9e, 0x9c, 0xac, 0x54, 0x9e, 0x9e, 0xac,
- 0x54, 0xfe, 0x3c, 0x59, 0xa9, 0x7c, 0xfd, 0xf7, 0xca, 0x99, 0x0f, 0xaf, 0xb4, 0x63, 0xb5, 0xec,
- 0x7a, 0x10, 0x6f, 0xe4, 0x23, 0xf2, 0xd6, 0x46, 0x71, 0x2b, 0x5a, 0x93, 0x6a, 0xf2, 0xdd, 0x7a,
- 0x16, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xa0, 0x15, 0x1f, 0x9b, 0x0b, 0x00, 0x00,
+func (x *InternalRaftRequest) GetDeleteRange() *DeleteRangeRequest {
+ if x != nil {
+ return x.DeleteRange
+ }
+ return nil
}
-func (m *RequestHeader) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *InternalRaftRequest) GetTxn() *TxnRequest {
+ if x != nil {
+ return x.Txn
}
- return dAtA[:n], nil
+ return nil
}
-func (m *RequestHeader) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *InternalRaftRequest) GetCompaction() *CompactionRequest {
+ if x != nil {
+ return x.Compaction
+ }
+ return nil
}
-func (m *RequestHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.AuthRevision != 0 {
- i = encodeVarintRaftInternal(dAtA, i, uint64(m.AuthRevision))
- i--
- dAtA[i] = 0x18
+func (x *InternalRaftRequest) GetLeaseGrant() *LeaseGrantRequest {
+ if x != nil {
+ return x.LeaseGrant
}
- if len(m.Username) > 0 {
- i -= len(m.Username)
- copy(dAtA[i:], m.Username)
- i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Username)))
- i--
- dAtA[i] = 0x12
- }
- if m.ID != 0 {
- i = encodeVarintRaftInternal(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
+ return nil
}
-func (m *InternalRaftRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *InternalRaftRequest) GetLeaseRevoke() *LeaseRevokeRequest {
+ if x != nil {
+ return x.LeaseRevoke
}
- return dAtA[:n], nil
+ return nil
}
-func (m *InternalRaftRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *InternalRaftRequest) GetAlarm() *AlarmRequest {
+ if x != nil {
+ return x.Alarm
+ }
+ return nil
}
-func (m *InternalRaftRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.DowngradeVersionTest != nil {
- {
- size, err := m.DowngradeVersionTest.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4
- i--
- dAtA[i] = 0xea
- i--
- dAtA[i] = 0xe2
- }
- if m.DowngradeInfoSet != nil {
- {
- size, err := m.DowngradeInfoSet.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x51
- i--
- dAtA[i] = 0xb2
- }
- if m.ClusterMemberAttrSet != nil {
- {
- size, err := m.ClusterMemberAttrSet.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x51
- i--
- dAtA[i] = 0xaa
- }
- if m.ClusterVersionSet != nil {
- {
- size, err := m.ClusterVersionSet.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x51
- i--
- dAtA[i] = 0xa2
- }
- if m.AuthRoleRevokePermission != nil {
- {
- size, err := m.AuthRoleRevokePermission.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4b
- i--
- dAtA[i] = 0xa2
- }
- if m.AuthRoleGrantPermission != nil {
- {
- size, err := m.AuthRoleGrantPermission.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4b
- i--
- dAtA[i] = 0x9a
- }
- if m.AuthRoleGet != nil {
- {
- size, err := m.AuthRoleGet.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4b
- i--
- dAtA[i] = 0x92
- }
- if m.AuthRoleDelete != nil {
- {
- size, err := m.AuthRoleDelete.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4b
- i--
- dAtA[i] = 0x8a
- }
- if m.AuthRoleAdd != nil {
- {
- size, err := m.AuthRoleAdd.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4b
- i--
- dAtA[i] = 0x82
- }
- if m.AuthRoleList != nil {
- {
- size, err := m.AuthRoleList.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x45
- i--
- dAtA[i] = 0x9a
- }
- if m.AuthUserList != nil {
- {
- size, err := m.AuthUserList.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x45
- i--
- dAtA[i] = 0x92
- }
- if m.AuthUserRevokeRole != nil {
- {
- size, err := m.AuthUserRevokeRole.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x45
- i--
- dAtA[i] = 0x8a
- }
- if m.AuthUserGrantRole != nil {
- {
- size, err := m.AuthUserGrantRole.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x45
- i--
- dAtA[i] = 0x82
- }
- if m.AuthUserChangePassword != nil {
- {
- size, err := m.AuthUserChangePassword.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x44
- i--
- dAtA[i] = 0xfa
- }
- if m.AuthUserGet != nil {
- {
- size, err := m.AuthUserGet.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x44
- i--
- dAtA[i] = 0xf2
- }
- if m.AuthUserDelete != nil {
- {
- size, err := m.AuthUserDelete.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x44
- i--
- dAtA[i] = 0xea
- }
- if m.AuthUserAdd != nil {
- {
- size, err := m.AuthUserAdd.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x44
- i--
- dAtA[i] = 0xe2
- }
- if m.AuthStatus != nil {
- {
- size, err := m.AuthStatus.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x3f
- i--
- dAtA[i] = 0xaa
- }
- if m.Authenticate != nil {
- {
- size, err := m.Authenticate.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x3f
- i--
- dAtA[i] = 0xa2
- }
- if m.AuthDisable != nil {
- {
- size, err := m.AuthDisable.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x3f
- i--
- dAtA[i] = 0x9a
- }
- if m.AuthEnable != nil {
- {
- size, err := m.AuthEnable.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x3e
- i--
- dAtA[i] = 0xc2
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x6
- i--
- dAtA[i] = 0xa2
- }
- if m.LeaseCheckpoint != nil {
- {
- size, err := m.LeaseCheckpoint.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x5a
- }
- if m.Alarm != nil {
- {
- size, err := m.Alarm.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x52
- }
- if m.LeaseRevoke != nil {
- {
- size, err := m.LeaseRevoke.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x4a
- }
- if m.LeaseGrant != nil {
- {
- size, err := m.LeaseGrant.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x42
- }
- if m.Compaction != nil {
- {
- size, err := m.Compaction.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x3a
- }
- if m.Txn != nil {
- {
- size, err := m.Txn.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x32
- }
- if m.DeleteRange != nil {
- {
- size, err := m.DeleteRange.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x2a
- }
- if m.Put != nil {
- {
- size, err := m.Put.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x22
- }
- if m.Range != nil {
- {
- size, err := m.Range.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- if m.V2 != nil {
- {
- size, err := m.V2.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRaftInternal(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.ID != 0 {
- i = encodeVarintRaftInternal(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
+func (x *InternalRaftRequest) GetLeaseCheckpoint() *LeaseCheckpointRequest {
+ if x != nil {
+ return x.LeaseCheckpoint
}
- return len(dAtA) - i, nil
+ return nil
}
-func (m *EmptyResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *InternalRaftRequest) GetAuthEnable() *AuthEnableRequest {
+ if x != nil {
+ return x.AuthEnable
}
- return dAtA[:n], nil
+ return nil
}
-func (m *EmptyResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *InternalRaftRequest) GetAuthDisable() *AuthDisableRequest {
+ if x != nil {
+ return x.AuthDisable
+ }
+ return nil
}
-func (m *EmptyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
+func (x *InternalRaftRequest) GetAuthStatus() *AuthStatusRequest {
+ if x != nil {
+ return x.AuthStatus
}
- return len(dAtA) - i, nil
+ return nil
}
-func (m *InternalAuthenticateRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *InternalRaftRequest) GetAuthenticate() *InternalAuthenticateRequest {
+ if x != nil {
+ return x.Authenticate
}
- return dAtA[:n], nil
+ return nil
}
-func (m *InternalAuthenticateRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *InternalRaftRequest) GetAuthUserAdd() *AuthUserAddRequest {
+ if x != nil {
+ return x.AuthUserAdd
+ }
+ return nil
}
-func (m *InternalAuthenticateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
+func (x *InternalRaftRequest) GetAuthUserDelete() *AuthUserDeleteRequest {
+ if x != nil {
+ return x.AuthUserDelete
}
- if len(m.SimpleToken) > 0 {
- i -= len(m.SimpleToken)
- copy(dAtA[i:], m.SimpleToken)
- i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.SimpleToken)))
- i--
- dAtA[i] = 0x1a
- }
- if len(m.Password) > 0 {
- i -= len(m.Password)
- copy(dAtA[i:], m.Password)
- i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Password)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRaftInternal(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+ return nil
}
-func encodeVarintRaftInternal(dAtA []byte, offset int, v uint64) int {
- offset -= sovRaftInternal(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
+func (x *InternalRaftRequest) GetAuthUserGet() *AuthUserGetRequest {
+ if x != nil {
+ return x.AuthUserGet
}
- dAtA[offset] = uint8(v)
- return base
+ return nil
}
-func (m *RequestHeader) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRaftInternal(uint64(m.ID))
- }
- l = len(m.Username)
- if l > 0 {
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRevision != 0 {
- n += 1 + sovRaftInternal(uint64(m.AuthRevision))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
+
+func (x *InternalRaftRequest) GetAuthUserChangePassword() *AuthUserChangePasswordRequest {
+ if x != nil {
+ return x.AuthUserChangePassword
}
- return n
+ return nil
}
-func (m *InternalRaftRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRaftInternal(uint64(m.ID))
- }
- if m.V2 != nil {
- l = m.V2.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Range != nil {
- l = m.Range.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Put != nil {
- l = m.Put.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.DeleteRange != nil {
- l = m.DeleteRange.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Txn != nil {
- l = m.Txn.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Compaction != nil {
- l = m.Compaction.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.LeaseGrant != nil {
- l = m.LeaseGrant.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.LeaseRevoke != nil {
- l = m.LeaseRevoke.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Alarm != nil {
- l = m.Alarm.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.LeaseCheckpoint != nil {
- l = m.LeaseCheckpoint.Size()
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.Header != nil {
- l = m.Header.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthEnable != nil {
- l = m.AuthEnable.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
+func (x *InternalRaftRequest) GetAuthUserGrantRole() *AuthUserGrantRoleRequest {
+ if x != nil {
+ return x.AuthUserGrantRole
}
- if m.AuthDisable != nil {
- l = m.AuthDisable.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.Authenticate != nil {
- l = m.Authenticate.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthStatus != nil {
- l = m.AuthStatus.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserAdd != nil {
- l = m.AuthUserAdd.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserDelete != nil {
- l = m.AuthUserDelete.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserGet != nil {
- l = m.AuthUserGet.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserChangePassword != nil {
- l = m.AuthUserChangePassword.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserGrantRole != nil {
- l = m.AuthUserGrantRole.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserRevokeRole != nil {
- l = m.AuthUserRevokeRole.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthUserList != nil {
- l = m.AuthUserList.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleList != nil {
- l = m.AuthRoleList.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleAdd != nil {
- l = m.AuthRoleAdd.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleDelete != nil {
- l = m.AuthRoleDelete.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleGet != nil {
- l = m.AuthRoleGet.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleGrantPermission != nil {
- l = m.AuthRoleGrantPermission.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.AuthRoleRevokePermission != nil {
- l = m.AuthRoleRevokePermission.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.ClusterVersionSet != nil {
- l = m.ClusterVersionSet.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.ClusterMemberAttrSet != nil {
- l = m.ClusterMemberAttrSet.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.DowngradeInfoSet != nil {
- l = m.DowngradeInfoSet.Size()
- n += 2 + l + sovRaftInternal(uint64(l))
- }
- if m.DowngradeVersionTest != nil {
- l = m.DowngradeVersionTest.Size()
- n += 3 + l + sovRaftInternal(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return nil
}
-func (m *EmptyResponse) Size() (n int) {
- if m == nil {
- return 0
+func (x *InternalRaftRequest) GetAuthUserRevokeRole() *AuthUserRevokeRoleRequest {
+ if x != nil {
+ return x.AuthUserRevokeRole
}
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return nil
}
-func (m *InternalAuthenticateRequest) Size() (n int) {
- if m == nil {
- return 0
+func (x *InternalRaftRequest) GetAuthUserList() *AuthUserListRequest {
+ if x != nil {
+ return x.AuthUserList
}
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- l = len(m.Password)
- if l > 0 {
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- l = len(m.SimpleToken)
- if l > 0 {
- n += 1 + l + sovRaftInternal(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return nil
}
-func sovRaftInternal(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
+func (x *InternalRaftRequest) GetAuthRoleList() *AuthRoleListRequest {
+ if x != nil {
+ return x.AuthRoleList
+ }
+ return nil
}
-func sozRaftInternal(x uint64) (n int) {
- return sovRaftInternal(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+
+func (x *InternalRaftRequest) GetAuthRoleAdd() *AuthRoleAddRequest {
+ if x != nil {
+ return x.AuthRoleAdd
+ }
+ return nil
}
-func (m *RequestHeader) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: RequestHeader: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: RequestHeader: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Username = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRevision", wireType)
- }
- m.AuthRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.AuthRevision |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRaftInternal(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
+
+func (x *InternalRaftRequest) GetAuthRoleDelete() *AuthRoleDeleteRequest {
+ if x != nil {
+ return x.AuthRoleDelete
}
+ return nil
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *InternalRaftRequest) GetAuthRoleGet() *AuthRoleGetRequest {
+ if x != nil {
+ return x.AuthRoleGet
}
return nil
}
-func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: InternalRaftRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: InternalRaftRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field V2", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.V2 == nil {
- m.V2 = &Request{}
- }
- if err := m.V2.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Range", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Range == nil {
- m.Range = &RangeRequest{}
- }
- if err := m.Range.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Put", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Put == nil {
- m.Put = &PutRequest{}
- }
- if err := m.Put.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 5:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field DeleteRange", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.DeleteRange == nil {
- m.DeleteRange = &DeleteRangeRequest{}
- }
- if err := m.DeleteRange.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 6:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Txn", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Txn == nil {
- m.Txn = &TxnRequest{}
- }
- if err := m.Txn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 7:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Compaction", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Compaction == nil {
- m.Compaction = &CompactionRequest{}
- }
- if err := m.Compaction.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 8:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field LeaseGrant", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.LeaseGrant == nil {
- m.LeaseGrant = &LeaseGrantRequest{}
- }
- if err := m.LeaseGrant.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 9:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field LeaseRevoke", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.LeaseRevoke == nil {
- m.LeaseRevoke = &LeaseRevokeRequest{}
- }
- if err := m.LeaseRevoke.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 10:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Alarm == nil {
- m.Alarm = &AlarmRequest{}
- }
- if err := m.Alarm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 11:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field LeaseCheckpoint", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.LeaseCheckpoint == nil {
- m.LeaseCheckpoint = &LeaseCheckpointRequest{}
- }
- if err := m.LeaseCheckpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 100:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &RequestHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1000:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthEnable", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthEnable == nil {
- m.AuthEnable = &AuthEnableRequest{}
- }
- if err := m.AuthEnable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1011:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthDisable", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthDisable == nil {
- m.AuthDisable = &AuthDisableRequest{}
- }
- if err := m.AuthDisable.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1012:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Authenticate", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Authenticate == nil {
- m.Authenticate = &InternalAuthenticateRequest{}
- }
- if err := m.Authenticate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1013:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthStatus", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthStatus == nil {
- m.AuthStatus = &AuthStatusRequest{}
- }
- if err := m.AuthStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1100:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserAdd", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserAdd == nil {
- m.AuthUserAdd = &AuthUserAddRequest{}
- }
- if err := m.AuthUserAdd.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1101:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserDelete", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserDelete == nil {
- m.AuthUserDelete = &AuthUserDeleteRequest{}
- }
- if err := m.AuthUserDelete.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1102:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserGet", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserGet == nil {
- m.AuthUserGet = &AuthUserGetRequest{}
- }
- if err := m.AuthUserGet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1103:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserChangePassword", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserChangePassword == nil {
- m.AuthUserChangePassword = &AuthUserChangePasswordRequest{}
- }
- if err := m.AuthUserChangePassword.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1104:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserGrantRole", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserGrantRole == nil {
- m.AuthUserGrantRole = &AuthUserGrantRoleRequest{}
- }
- if err := m.AuthUserGrantRole.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1105:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserRevokeRole", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserRevokeRole == nil {
- m.AuthUserRevokeRole = &AuthUserRevokeRoleRequest{}
- }
- if err := m.AuthUserRevokeRole.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1106:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthUserList", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthUserList == nil {
- m.AuthUserList = &AuthUserListRequest{}
- }
- if err := m.AuthUserList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1107:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleList", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleList == nil {
- m.AuthRoleList = &AuthRoleListRequest{}
- }
- if err := m.AuthRoleList.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1200:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleAdd", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleAdd == nil {
- m.AuthRoleAdd = &AuthRoleAddRequest{}
- }
- if err := m.AuthRoleAdd.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1201:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleDelete", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleDelete == nil {
- m.AuthRoleDelete = &AuthRoleDeleteRequest{}
- }
- if err := m.AuthRoleDelete.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1202:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleGet", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleGet == nil {
- m.AuthRoleGet = &AuthRoleGetRequest{}
- }
- if err := m.AuthRoleGet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1203:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleGrantPermission", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleGrantPermission == nil {
- m.AuthRoleGrantPermission = &AuthRoleGrantPermissionRequest{}
- }
- if err := m.AuthRoleGrantPermission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1204:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRoleRevokePermission", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.AuthRoleRevokePermission == nil {
- m.AuthRoleRevokePermission = &AuthRoleRevokePermissionRequest{}
- }
- if err := m.AuthRoleRevokePermission.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1300:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClusterVersionSet", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.ClusterVersionSet == nil {
- m.ClusterVersionSet = &membershippb.ClusterVersionSetRequest{}
- }
- if err := m.ClusterVersionSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1301:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClusterMemberAttrSet", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.ClusterMemberAttrSet == nil {
- m.ClusterMemberAttrSet = &membershippb.ClusterMemberAttrSetRequest{}
- }
- if err := m.ClusterMemberAttrSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 1302:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field DowngradeInfoSet", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.DowngradeInfoSet == nil {
- m.DowngradeInfoSet = &membershippb.DowngradeInfoSetRequest{}
- }
- if err := m.DowngradeInfoSet.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 9900:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field DowngradeVersionTest", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.DowngradeVersionTest == nil {
- m.DowngradeVersionTest = &DowngradeVersionTestRequest{}
- }
- if err := m.DowngradeVersionTest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRaftInternal(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
+
+func (x *InternalRaftRequest) GetAuthRoleGrantPermission() *AuthRoleGrantPermissionRequest {
+ if x != nil {
+ return x.AuthRoleGrantPermission
}
+ return nil
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *InternalRaftRequest) GetAuthRoleRevokePermission() *AuthRoleRevokePermissionRequest {
+ if x != nil {
+ return x.AuthRoleRevokePermission
}
return nil
}
-func (m *EmptyResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: EmptyResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: EmptyResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRaftInternal(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
+
+func (x *InternalRaftRequest) GetClusterVersionSet() *membershippb.ClusterVersionSetRequest {
+ if x != nil {
+ return x.ClusterVersionSet
}
+ return nil
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *InternalRaftRequest) GetClusterMemberAttrSet() *membershippb.ClusterMemberAttrSetRequest {
+ if x != nil {
+ return x.ClusterMemberAttrSet
}
return nil
}
-func (m *InternalAuthenticateRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: InternalAuthenticateRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: InternalAuthenticateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Password = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field SimpleToken", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRaftInternal
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.SimpleToken = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRaftInternal(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRaftInternal
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
+
+func (x *InternalRaftRequest) GetDowngradeInfoSet() *membershippb.DowngradeInfoSetRequest {
+ if x != nil {
+ return x.DowngradeInfoSet
}
+ return nil
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *InternalRaftRequest) GetDowngradeVersionTest() *DowngradeVersionTestRequest {
+ if x != nil {
+ return x.DowngradeVersionTest
}
return nil
}
-func skipRaftInternal(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRaftInternal
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthRaftInternal
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupRaftInternal
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthRaftInternal
+
+type EmptyResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *EmptyResponse) Reset() {
+ *x = EmptyResponse{}
+ mi := &file_raft_internal_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *EmptyResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EmptyResponse) ProtoMessage() {}
+
+func (x *EmptyResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_raft_internal_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- if depth == 0 {
- return iNdEx, nil
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead.
+func (*EmptyResponse) Descriptor() ([]byte, []int) {
+ return file_raft_internal_proto_rawDescGZIP(), []int{2}
+}
+
+// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
+// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
+// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
+type InternalAuthenticateRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ // simple_token is generated in API layer (etcdserver/v3_server.go)
+ SimpleToken string `protobuf:"bytes,3,opt,name=simple_token,json=simpleToken,proto3" json:"simple_token,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *InternalAuthenticateRequest) Reset() {
+ *x = InternalAuthenticateRequest{}
+ mi := &file_raft_internal_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *InternalAuthenticateRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InternalAuthenticateRequest) ProtoMessage() {}
+
+func (x *InternalAuthenticateRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_raft_internal_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
- return 0, io.ErrUnexpectedEOF
+ return mi.MessageOf(x)
}
+// Deprecated: Use InternalAuthenticateRequest.ProtoReflect.Descriptor instead.
+func (*InternalAuthenticateRequest) Descriptor() ([]byte, []int) {
+ return file_raft_internal_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *InternalAuthenticateRequest) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *InternalAuthenticateRequest) GetPassword() string {
+ if x != nil {
+ return x.Password
+ }
+ return ""
+}
+
+func (x *InternalAuthenticateRequest) GetSimpleToken() string {
+ if x != nil {
+ return x.SimpleToken
+ }
+ return ""
+}
+
+var File_raft_internal_proto protoreflect.FileDescriptor
+
+const file_raft_internal_proto_rawDesc = "" +
+ "\n" +
+ "\x13raft_internal.proto\x12\fetcdserverpb\x1a\trpc.proto\x1a etcd/api/versionpb/version.proto\x1a&etcd/api/membershippb/membership.proto\"r\n" +
+ "\rRequestHeader\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID\x12\x1a\n" +
+ "\busername\x18\x02 \x01(\tR\busername\x12,\n" +
+ "\rauth_revision\x18\x03 \x01(\x04B\a\x8a\xb5\x18\x033.1R\fauthRevision:\a\x82\xb5\x18\x033.0\"\x98\x13\n" +
+ "\x13InternalRaftRequest\x123\n" +
+ "\x06header\x18d \x01(\v2\x1b.etcdserverpb.RequestHeaderR\x06header\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID\x120\n" +
+ "\x05range\x18\x03 \x01(\v2\x1a.etcdserverpb.RangeRequestR\x05range\x12*\n" +
+ "\x03put\x18\x04 \x01(\v2\x18.etcdserverpb.PutRequestR\x03put\x12C\n" +
+ "\fdelete_range\x18\x05 \x01(\v2 .etcdserverpb.DeleteRangeRequestR\vdeleteRange\x12*\n" +
+ "\x03txn\x18\x06 \x01(\v2\x18.etcdserverpb.TxnRequestR\x03txn\x12?\n" +
+ "\n" +
+ "compaction\x18\a \x01(\v2\x1f.etcdserverpb.CompactionRequestR\n" +
+ "compaction\x12@\n" +
+ "\vlease_grant\x18\b \x01(\v2\x1f.etcdserverpb.LeaseGrantRequestR\n" +
+ "leaseGrant\x12C\n" +
+ "\flease_revoke\x18\t \x01(\v2 .etcdserverpb.LeaseRevokeRequestR\vleaseRevoke\x120\n" +
+ "\x05alarm\x18\n" +
+ " \x01(\v2\x1a.etcdserverpb.AlarmRequestR\x05alarm\x12X\n" +
+ "\x10lease_checkpoint\x18\v \x01(\v2$.etcdserverpb.LeaseCheckpointRequestB\a\x8a\xb5\x18\x033.4R\x0fleaseCheckpoint\x12A\n" +
+ "\vauth_enable\x18\xe8\a \x01(\v2\x1f.etcdserverpb.AuthEnableRequestR\n" +
+ "authEnable\x12D\n" +
+ "\fauth_disable\x18\xf3\a \x01(\v2 .etcdserverpb.AuthDisableRequestR\vauthDisable\x12J\n" +
+ "\vauth_status\x18\xf5\a \x01(\v2\x1f.etcdserverpb.AuthStatusRequestB\a\x8a\xb5\x18\x033.5R\n" +
+ "authStatus\x12N\n" +
+ "\fauthenticate\x18\xf4\a \x01(\v2).etcdserverpb.InternalAuthenticateRequestR\fauthenticate\x12E\n" +
+ "\rauth_user_add\x18\xcc\b \x01(\v2 .etcdserverpb.AuthUserAddRequestR\vauthUserAdd\x12N\n" +
+ "\x10auth_user_delete\x18\xcd\b \x01(\v2#.etcdserverpb.AuthUserDeleteRequestR\x0eauthUserDelete\x12E\n" +
+ "\rauth_user_get\x18\xce\b \x01(\v2 .etcdserverpb.AuthUserGetRequestR\vauthUserGet\x12g\n" +
+ "\x19auth_user_change_password\x18\xcf\b \x01(\v2+.etcdserverpb.AuthUserChangePasswordRequestR\x16authUserChangePassword\x12X\n" +
+ "\x14auth_user_grant_role\x18\xd0\b \x01(\v2&.etcdserverpb.AuthUserGrantRoleRequestR\x11authUserGrantRole\x12[\n" +
+ "\x15auth_user_revoke_role\x18\xd1\b \x01(\v2'.etcdserverpb.AuthUserRevokeRoleRequestR\x12authUserRevokeRole\x12H\n" +
+ "\x0eauth_user_list\x18\xd2\b \x01(\v2!.etcdserverpb.AuthUserListRequestR\fauthUserList\x12H\n" +
+ "\x0eauth_role_list\x18\xd3\b \x01(\v2!.etcdserverpb.AuthRoleListRequestR\fauthRoleList\x12E\n" +
+ "\rauth_role_add\x18\xb0\t \x01(\v2 .etcdserverpb.AuthRoleAddRequestR\vauthRoleAdd\x12N\n" +
+ "\x10auth_role_delete\x18\xb1\t \x01(\v2#.etcdserverpb.AuthRoleDeleteRequestR\x0eauthRoleDelete\x12E\n" +
+ "\rauth_role_get\x18\xb2\t \x01(\v2 .etcdserverpb.AuthRoleGetRequestR\vauthRoleGet\x12j\n" +
+ "\x1aauth_role_grant_permission\x18\xb3\t \x01(\v2,.etcdserverpb.AuthRoleGrantPermissionRequestR\x17authRoleGrantPermission\x12m\n" +
+ "\x1bauth_role_revoke_permission\x18\xb4\t \x01(\v2-.etcdserverpb.AuthRoleRevokePermissionRequestR\x18authRoleRevokePermission\x12`\n" +
+ "\x13cluster_version_set\x18\x94\n" +
+ " \x01(\v2&.membershippb.ClusterVersionSetRequestB\a\x8a\xb5\x18\x033.5R\x11clusterVersionSet\x12j\n" +
+ "\x17cluster_member_attr_set\x18\x95\n" +
+ " \x01(\v2).membershippb.ClusterMemberAttrSetRequestB\a\x8a\xb5\x18\x033.5R\x14clusterMemberAttrSet\x12]\n" +
+ "\x12downgrade_info_set\x18\x96\n" +
+ " \x01(\v2%.membershippb.DowngradeInfoSetRequestB\a\x8a\xb5\x18\x033.5R\x10downgradeInfoSet\x12i\n" +
+ "\x16downgrade_version_test\x18\xacM \x01(\v2).etcdserverpb.DowngradeVersionTestRequestB\a\x8a\xb5\x18\x033.6R\x14downgradeVersionTest:\a\x82\xb5\x18\x033.0J\x04\b\x02\x10\x03R\x02v2\"\x0f\n" +
+ "\rEmptyResponse\"y\n" +
+ "\x1bInternalAuthenticateRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" +
+ "\bpassword\x18\x02 \x01(\tR\bpassword\x12!\n" +
+ "\fsimple_token\x18\x03 \x01(\tR\vsimpleToken:\a\x82\xb5\x18\x033.0B%Z#go.etcd.io/etcd/api/v3/etcdserverpbb\x06proto3"
+
var (
- ErrInvalidLengthRaftInternal = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowRaftInternal = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupRaftInternal = fmt.Errorf("proto: unexpected end of group")
+ file_raft_internal_proto_rawDescOnce sync.Once
+ file_raft_internal_proto_rawDescData []byte
)
+
+func file_raft_internal_proto_rawDescGZIP() []byte {
+ file_raft_internal_proto_rawDescOnce.Do(func() {
+ file_raft_internal_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_raft_internal_proto_rawDesc), len(file_raft_internal_proto_rawDesc)))
+ })
+ return file_raft_internal_proto_rawDescData
+}
+
+var file_raft_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_raft_internal_proto_goTypes = []any{
+ (*RequestHeader)(nil), // 0: etcdserverpb.RequestHeader
+ (*InternalRaftRequest)(nil), // 1: etcdserverpb.InternalRaftRequest
+ (*EmptyResponse)(nil), // 2: etcdserverpb.EmptyResponse
+ (*InternalAuthenticateRequest)(nil), // 3: etcdserverpb.InternalAuthenticateRequest
+ (*RangeRequest)(nil), // 4: etcdserverpb.RangeRequest
+ (*PutRequest)(nil), // 5: etcdserverpb.PutRequest
+ (*DeleteRangeRequest)(nil), // 6: etcdserverpb.DeleteRangeRequest
+ (*TxnRequest)(nil), // 7: etcdserverpb.TxnRequest
+ (*CompactionRequest)(nil), // 8: etcdserverpb.CompactionRequest
+ (*LeaseGrantRequest)(nil), // 9: etcdserverpb.LeaseGrantRequest
+ (*LeaseRevokeRequest)(nil), // 10: etcdserverpb.LeaseRevokeRequest
+ (*AlarmRequest)(nil), // 11: etcdserverpb.AlarmRequest
+ (*LeaseCheckpointRequest)(nil), // 12: etcdserverpb.LeaseCheckpointRequest
+ (*AuthEnableRequest)(nil), // 13: etcdserverpb.AuthEnableRequest
+ (*AuthDisableRequest)(nil), // 14: etcdserverpb.AuthDisableRequest
+ (*AuthStatusRequest)(nil), // 15: etcdserverpb.AuthStatusRequest
+ (*AuthUserAddRequest)(nil), // 16: etcdserverpb.AuthUserAddRequest
+ (*AuthUserDeleteRequest)(nil), // 17: etcdserverpb.AuthUserDeleteRequest
+ (*AuthUserGetRequest)(nil), // 18: etcdserverpb.AuthUserGetRequest
+ (*AuthUserChangePasswordRequest)(nil), // 19: etcdserverpb.AuthUserChangePasswordRequest
+ (*AuthUserGrantRoleRequest)(nil), // 20: etcdserverpb.AuthUserGrantRoleRequest
+ (*AuthUserRevokeRoleRequest)(nil), // 21: etcdserverpb.AuthUserRevokeRoleRequest
+ (*AuthUserListRequest)(nil), // 22: etcdserverpb.AuthUserListRequest
+ (*AuthRoleListRequest)(nil), // 23: etcdserverpb.AuthRoleListRequest
+ (*AuthRoleAddRequest)(nil), // 24: etcdserverpb.AuthRoleAddRequest
+ (*AuthRoleDeleteRequest)(nil), // 25: etcdserverpb.AuthRoleDeleteRequest
+ (*AuthRoleGetRequest)(nil), // 26: etcdserverpb.AuthRoleGetRequest
+ (*AuthRoleGrantPermissionRequest)(nil), // 27: etcdserverpb.AuthRoleGrantPermissionRequest
+ (*AuthRoleRevokePermissionRequest)(nil), // 28: etcdserverpb.AuthRoleRevokePermissionRequest
+ (*membershippb.ClusterVersionSetRequest)(nil), // 29: membershippb.ClusterVersionSetRequest
+ (*membershippb.ClusterMemberAttrSetRequest)(nil), // 30: membershippb.ClusterMemberAttrSetRequest
+ (*membershippb.DowngradeInfoSetRequest)(nil), // 31: membershippb.DowngradeInfoSetRequest
+ (*DowngradeVersionTestRequest)(nil), // 32: etcdserverpb.DowngradeVersionTestRequest
+}
+var file_raft_internal_proto_depIdxs = []int32{
+ 0, // 0: etcdserverpb.InternalRaftRequest.header:type_name -> etcdserverpb.RequestHeader
+ 4, // 1: etcdserverpb.InternalRaftRequest.range:type_name -> etcdserverpb.RangeRequest
+ 5, // 2: etcdserverpb.InternalRaftRequest.put:type_name -> etcdserverpb.PutRequest
+ 6, // 3: etcdserverpb.InternalRaftRequest.delete_range:type_name -> etcdserverpb.DeleteRangeRequest
+ 7, // 4: etcdserverpb.InternalRaftRequest.txn:type_name -> etcdserverpb.TxnRequest
+ 8, // 5: etcdserverpb.InternalRaftRequest.compaction:type_name -> etcdserverpb.CompactionRequest
+ 9, // 6: etcdserverpb.InternalRaftRequest.lease_grant:type_name -> etcdserverpb.LeaseGrantRequest
+ 10, // 7: etcdserverpb.InternalRaftRequest.lease_revoke:type_name -> etcdserverpb.LeaseRevokeRequest
+ 11, // 8: etcdserverpb.InternalRaftRequest.alarm:type_name -> etcdserverpb.AlarmRequest
+ 12, // 9: etcdserverpb.InternalRaftRequest.lease_checkpoint:type_name -> etcdserverpb.LeaseCheckpointRequest
+ 13, // 10: etcdserverpb.InternalRaftRequest.auth_enable:type_name -> etcdserverpb.AuthEnableRequest
+ 14, // 11: etcdserverpb.InternalRaftRequest.auth_disable:type_name -> etcdserverpb.AuthDisableRequest
+ 15, // 12: etcdserverpb.InternalRaftRequest.auth_status:type_name -> etcdserverpb.AuthStatusRequest
+ 3, // 13: etcdserverpb.InternalRaftRequest.authenticate:type_name -> etcdserverpb.InternalAuthenticateRequest
+ 16, // 14: etcdserverpb.InternalRaftRequest.auth_user_add:type_name -> etcdserverpb.AuthUserAddRequest
+ 17, // 15: etcdserverpb.InternalRaftRequest.auth_user_delete:type_name -> etcdserverpb.AuthUserDeleteRequest
+ 18, // 16: etcdserverpb.InternalRaftRequest.auth_user_get:type_name -> etcdserverpb.AuthUserGetRequest
+ 19, // 17: etcdserverpb.InternalRaftRequest.auth_user_change_password:type_name -> etcdserverpb.AuthUserChangePasswordRequest
+ 20, // 18: etcdserverpb.InternalRaftRequest.auth_user_grant_role:type_name -> etcdserverpb.AuthUserGrantRoleRequest
+ 21, // 19: etcdserverpb.InternalRaftRequest.auth_user_revoke_role:type_name -> etcdserverpb.AuthUserRevokeRoleRequest
+ 22, // 20: etcdserverpb.InternalRaftRequest.auth_user_list:type_name -> etcdserverpb.AuthUserListRequest
+ 23, // 21: etcdserverpb.InternalRaftRequest.auth_role_list:type_name -> etcdserverpb.AuthRoleListRequest
+ 24, // 22: etcdserverpb.InternalRaftRequest.auth_role_add:type_name -> etcdserverpb.AuthRoleAddRequest
+ 25, // 23: etcdserverpb.InternalRaftRequest.auth_role_delete:type_name -> etcdserverpb.AuthRoleDeleteRequest
+ 26, // 24: etcdserverpb.InternalRaftRequest.auth_role_get:type_name -> etcdserverpb.AuthRoleGetRequest
+ 27, // 25: etcdserverpb.InternalRaftRequest.auth_role_grant_permission:type_name -> etcdserverpb.AuthRoleGrantPermissionRequest
+ 28, // 26: etcdserverpb.InternalRaftRequest.auth_role_revoke_permission:type_name -> etcdserverpb.AuthRoleRevokePermissionRequest
+ 29, // 27: etcdserverpb.InternalRaftRequest.cluster_version_set:type_name -> membershippb.ClusterVersionSetRequest
+ 30, // 28: etcdserverpb.InternalRaftRequest.cluster_member_attr_set:type_name -> membershippb.ClusterMemberAttrSetRequest
+ 31, // 29: etcdserverpb.InternalRaftRequest.downgrade_info_set:type_name -> membershippb.DowngradeInfoSetRequest
+ 32, // 30: etcdserverpb.InternalRaftRequest.downgrade_version_test:type_name -> etcdserverpb.DowngradeVersionTestRequest
+ 31, // [31:31] is the sub-list for method output_type
+ 31, // [31:31] is the sub-list for method input_type
+ 31, // [31:31] is the sub-list for extension type_name
+ 31, // [31:31] is the sub-list for extension extendee
+ 0, // [0:31] is the sub-list for field type_name
+}
+
+func init() { file_raft_internal_proto_init() }
+func file_raft_internal_proto_init() {
+ if File_raft_internal_proto != nil {
+ return
+ }
+ file_rpc_proto_init()
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_raft_internal_proto_rawDesc), len(file_raft_internal_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_raft_internal_proto_goTypes,
+ DependencyIndexes: file_raft_internal_proto_depIdxs,
+ MessageInfos: file_raft_internal_proto_msgTypes,
+ }.Build()
+ File_raft_internal_proto = out.File
+ file_raft_internal_proto_goTypes = nil
+ file_raft_internal_proto_depIdxs = nil
+}
diff --git a/api/etcdserverpb/raft_internal.proto b/api/etcdserverpb/raft_internal.proto
index 88b8ab5279ef..51767ea9ecab 100644
--- a/api/etcdserverpb/raft_internal.proto
+++ b/api/etcdserverpb/raft_internal.proto
@@ -1,19 +1,12 @@
syntax = "proto3";
package etcdserverpb;
-import "gogoproto/gogo.proto";
-import "etcdserver.proto";
import "rpc.proto";
import "etcd/api/versionpb/version.proto";
import "etcd/api/membershippb/membership.proto";
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-
message RequestHeader {
option (versionpb.etcd_version_msg) = "3.0";
@@ -32,7 +25,8 @@ message InternalRaftRequest {
RequestHeader header = 100;
uint64 ID = 1;
- Request v2 = 2;
+ reserved 2;
+ reserved "v2";
RangeRequest range = 3;
PutRequest put = 4;
diff --git a/api/etcdserverpb/raft_internal_stringer.go b/api/etcdserverpb/raft_internal_stringer.go
index a9431d52542b..05c4263ae95b 100644
--- a/api/etcdserverpb/raft_internal_stringer.go
+++ b/api/etcdserverpb/raft_internal_stringer.go
@@ -18,7 +18,7 @@ import (
"fmt"
"strings"
- proto "github.com/golang/protobuf/proto"
+ proto "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
)
// InternalRaftStringer implements custom proto Stringer:
diff --git a/api/etcdserverpb/rpc.pb.go b/api/etcdserverpb/rpc.pb.go
index 42bf641db6b5..5e13278953b8 100644
--- a/api/etcdserverpb/rpc.pb.go
+++ b/api/etcdserverpb/rpc.pb.go
@@ -1,92 +1,128 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: rpc.proto
package etcdserverpb
import (
- context "context"
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
-
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
+
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
authpb "go.etcd.io/etcd/api/v3/authpb"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
_ "go.etcd.io/etcd/api/v3/versionpb"
_ "google.golang.org/genproto/googleapis/api/annotations"
- grpc "google.golang.org/grpc"
- codes "google.golang.org/grpc/codes"
- status "google.golang.org/grpc/status"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type AlarmType int32
const (
- AlarmType_NONE AlarmType = 0
- AlarmType_NOSPACE AlarmType = 1
- AlarmType_CORRUPT AlarmType = 2
+ AlarmType_NONE AlarmType = 0 // default, used to query if any alarm is active
+ AlarmType_NOSPACE AlarmType = 1 // space quota is exhausted
+ AlarmType_CORRUPT AlarmType = 2 // kv store corruption detected
)
-var AlarmType_name = map[int32]string{
- 0: "NONE",
- 1: "NOSPACE",
- 2: "CORRUPT",
-}
+// Enum value maps for AlarmType.
+var (
+ AlarmType_name = map[int32]string{
+ 0: "NONE",
+ 1: "NOSPACE",
+ 2: "CORRUPT",
+ }
+ AlarmType_value = map[string]int32{
+ "NONE": 0,
+ "NOSPACE": 1,
+ "CORRUPT": 2,
+ }
+)
-var AlarmType_value = map[string]int32{
- "NONE": 0,
- "NOSPACE": 1,
- "CORRUPT": 2,
+func (x AlarmType) Enum() *AlarmType {
+ p := new(AlarmType)
+ *p = x
+ return p
}
func (x AlarmType) String() string {
- return proto.EnumName(AlarmType_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AlarmType) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[0].Descriptor()
+}
+
+func (AlarmType) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[0]
+}
+
+func (x AlarmType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
+// Deprecated: Use AlarmType.Descriptor instead.
func (AlarmType) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{0}
+ return file_rpc_proto_rawDescGZIP(), []int{0}
}
type RangeRequest_SortOrder int32
const (
- RangeRequest_NONE RangeRequest_SortOrder = 0
- RangeRequest_ASCEND RangeRequest_SortOrder = 1
- RangeRequest_DESCEND RangeRequest_SortOrder = 2
+ RangeRequest_NONE RangeRequest_SortOrder = 0 // default, no sorting
+ RangeRequest_ASCEND RangeRequest_SortOrder = 1 // lowest target value first
+ RangeRequest_DESCEND RangeRequest_SortOrder = 2 // highest target value first
)
-var RangeRequest_SortOrder_name = map[int32]string{
- 0: "NONE",
- 1: "ASCEND",
- 2: "DESCEND",
-}
+// Enum value maps for RangeRequest_SortOrder.
+var (
+ RangeRequest_SortOrder_name = map[int32]string{
+ 0: "NONE",
+ 1: "ASCEND",
+ 2: "DESCEND",
+ }
+ RangeRequest_SortOrder_value = map[string]int32{
+ "NONE": 0,
+ "ASCEND": 1,
+ "DESCEND": 2,
+ }
+)
-var RangeRequest_SortOrder_value = map[string]int32{
- "NONE": 0,
- "ASCEND": 1,
- "DESCEND": 2,
+func (x RangeRequest_SortOrder) Enum() *RangeRequest_SortOrder {
+ p := new(RangeRequest_SortOrder)
+ *p = x
+ return p
}
func (x RangeRequest_SortOrder) String() string {
- return proto.EnumName(RangeRequest_SortOrder_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RangeRequest_SortOrder) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[1].Descriptor()
+}
+
+func (RangeRequest_SortOrder) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[1]
}
+func (x RangeRequest_SortOrder) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RangeRequest_SortOrder.Descriptor instead.
func (RangeRequest_SortOrder) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{1, 0}
+ return file_rpc_proto_rawDescGZIP(), []int{1, 0}
}
type RangeRequest_SortTarget int32
@@ -99,28 +135,49 @@ const (
RangeRequest_VALUE RangeRequest_SortTarget = 4
)
-var RangeRequest_SortTarget_name = map[int32]string{
- 0: "KEY",
- 1: "VERSION",
- 2: "CREATE",
- 3: "MOD",
- 4: "VALUE",
-}
+// Enum value maps for RangeRequest_SortTarget.
+var (
+ RangeRequest_SortTarget_name = map[int32]string{
+ 0: "KEY",
+ 1: "VERSION",
+ 2: "CREATE",
+ 3: "MOD",
+ 4: "VALUE",
+ }
+ RangeRequest_SortTarget_value = map[string]int32{
+ "KEY": 0,
+ "VERSION": 1,
+ "CREATE": 2,
+ "MOD": 3,
+ "VALUE": 4,
+ }
+)
-var RangeRequest_SortTarget_value = map[string]int32{
- "KEY": 0,
- "VERSION": 1,
- "CREATE": 2,
- "MOD": 3,
- "VALUE": 4,
+func (x RangeRequest_SortTarget) Enum() *RangeRequest_SortTarget {
+ p := new(RangeRequest_SortTarget)
+ *p = x
+ return p
}
func (x RangeRequest_SortTarget) String() string {
- return proto.EnumName(RangeRequest_SortTarget_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RangeRequest_SortTarget) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[2].Descriptor()
}
+func (RangeRequest_SortTarget) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[2]
+}
+
+func (x RangeRequest_SortTarget) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RangeRequest_SortTarget.Descriptor instead.
func (RangeRequest_SortTarget) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{1, 1}
+ return file_rpc_proto_rawDescGZIP(), []int{1, 1}
}
type Compare_CompareResult int32
@@ -132,26 +189,47 @@ const (
Compare_NOT_EQUAL Compare_CompareResult = 3
)
-var Compare_CompareResult_name = map[int32]string{
- 0: "EQUAL",
- 1: "GREATER",
- 2: "LESS",
- 3: "NOT_EQUAL",
-}
+// Enum value maps for Compare_CompareResult.
+var (
+ Compare_CompareResult_name = map[int32]string{
+ 0: "EQUAL",
+ 1: "GREATER",
+ 2: "LESS",
+ 3: "NOT_EQUAL",
+ }
+ Compare_CompareResult_value = map[string]int32{
+ "EQUAL": 0,
+ "GREATER": 1,
+ "LESS": 2,
+ "NOT_EQUAL": 3,
+ }
+)
-var Compare_CompareResult_value = map[string]int32{
- "EQUAL": 0,
- "GREATER": 1,
- "LESS": 2,
- "NOT_EQUAL": 3,
+func (x Compare_CompareResult) Enum() *Compare_CompareResult {
+ p := new(Compare_CompareResult)
+ *p = x
+ return p
}
func (x Compare_CompareResult) String() string {
- return proto.EnumName(Compare_CompareResult_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
+func (Compare_CompareResult) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[3].Descriptor()
+}
+
+func (Compare_CompareResult) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[3]
+}
+
+func (x Compare_CompareResult) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Compare_CompareResult.Descriptor instead.
func (Compare_CompareResult) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{9, 0}
+ return file_rpc_proto_rawDescGZIP(), []int{9, 0}
}
type Compare_CompareTarget int32
@@ -164,28 +242,49 @@ const (
Compare_LEASE Compare_CompareTarget = 4
)
-var Compare_CompareTarget_name = map[int32]string{
- 0: "VERSION",
- 1: "CREATE",
- 2: "MOD",
- 3: "VALUE",
- 4: "LEASE",
-}
+// Enum value maps for Compare_CompareTarget.
+var (
+ Compare_CompareTarget_name = map[int32]string{
+ 0: "VERSION",
+ 1: "CREATE",
+ 2: "MOD",
+ 3: "VALUE",
+ 4: "LEASE",
+ }
+ Compare_CompareTarget_value = map[string]int32{
+ "VERSION": 0,
+ "CREATE": 1,
+ "MOD": 2,
+ "VALUE": 3,
+ "LEASE": 4,
+ }
+)
-var Compare_CompareTarget_value = map[string]int32{
- "VERSION": 0,
- "CREATE": 1,
- "MOD": 2,
- "VALUE": 3,
- "LEASE": 4,
+func (x Compare_CompareTarget) Enum() *Compare_CompareTarget {
+ p := new(Compare_CompareTarget)
+ *p = x
+ return p
}
func (x Compare_CompareTarget) String() string {
- return proto.EnumName(Compare_CompareTarget_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Compare_CompareTarget) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[4].Descriptor()
+}
+
+func (Compare_CompareTarget) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[4]
+}
+
+func (x Compare_CompareTarget) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
+// Deprecated: Use Compare_CompareTarget.Descriptor instead.
func (Compare_CompareTarget) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{9, 1}
+ return file_rpc_proto_rawDescGZIP(), []int{9, 1}
}
type WatchCreateRequest_FilterType int32
@@ -197,22 +296,43 @@ const (
WatchCreateRequest_NODELETE WatchCreateRequest_FilterType = 1
)
-var WatchCreateRequest_FilterType_name = map[int32]string{
- 0: "NOPUT",
- 1: "NODELETE",
-}
+// Enum value maps for WatchCreateRequest_FilterType.
+var (
+ WatchCreateRequest_FilterType_name = map[int32]string{
+ 0: "NOPUT",
+ 1: "NODELETE",
+ }
+ WatchCreateRequest_FilterType_value = map[string]int32{
+ "NOPUT": 0,
+ "NODELETE": 1,
+ }
+)
-var WatchCreateRequest_FilterType_value = map[string]int32{
- "NOPUT": 0,
- "NODELETE": 1,
+func (x WatchCreateRequest_FilterType) Enum() *WatchCreateRequest_FilterType {
+ p := new(WatchCreateRequest_FilterType)
+ *p = x
+ return p
}
func (x WatchCreateRequest_FilterType) String() string {
- return proto.EnumName(WatchCreateRequest_FilterType_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (WatchCreateRequest_FilterType) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[5].Descriptor()
+}
+
+func (WatchCreateRequest_FilterType) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[5]
+}
+
+func (x WatchCreateRequest_FilterType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
+// Deprecated: Use WatchCreateRequest_FilterType.Descriptor instead.
func (WatchCreateRequest_FilterType) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{21, 0}
+ return file_rpc_proto_rawDescGZIP(), []int{21, 0}
}
type AlarmRequest_AlarmAction int32
@@ -223,24 +343,45 @@ const (
AlarmRequest_DEACTIVATE AlarmRequest_AlarmAction = 2
)
-var AlarmRequest_AlarmAction_name = map[int32]string{
- 0: "GET",
- 1: "ACTIVATE",
- 2: "DEACTIVATE",
-}
+// Enum value maps for AlarmRequest_AlarmAction.
+var (
+ AlarmRequest_AlarmAction_name = map[int32]string{
+ 0: "GET",
+ 1: "ACTIVATE",
+ 2: "DEACTIVATE",
+ }
+ AlarmRequest_AlarmAction_value = map[string]int32{
+ "GET": 0,
+ "ACTIVATE": 1,
+ "DEACTIVATE": 2,
+ }
+)
-var AlarmRequest_AlarmAction_value = map[string]int32{
- "GET": 0,
- "ACTIVATE": 1,
- "DEACTIVATE": 2,
+func (x AlarmRequest_AlarmAction) Enum() *AlarmRequest_AlarmAction {
+ p := new(AlarmRequest_AlarmAction)
+ *p = x
+ return p
}
func (x AlarmRequest_AlarmAction) String() string {
- return proto.EnumName(AlarmRequest_AlarmAction_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
+func (AlarmRequest_AlarmAction) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[6].Descriptor()
+}
+
+func (AlarmRequest_AlarmAction) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[6]
+}
+
+func (x AlarmRequest_AlarmAction) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AlarmRequest_AlarmAction.Descriptor instead.
func (AlarmRequest_AlarmAction) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{54, 0}
+ return file_rpc_proto_rawDescGZIP(), []int{54, 0}
}
type DowngradeRequest_DowngradeAction int32
@@ -251,27 +392,49 @@ const (
DowngradeRequest_CANCEL DowngradeRequest_DowngradeAction = 2
)
-var DowngradeRequest_DowngradeAction_name = map[int32]string{
- 0: "VALIDATE",
- 1: "ENABLE",
- 2: "CANCEL",
-}
+// Enum value maps for DowngradeRequest_DowngradeAction.
+var (
+ DowngradeRequest_DowngradeAction_name = map[int32]string{
+ 0: "VALIDATE",
+ 1: "ENABLE",
+ 2: "CANCEL",
+ }
+ DowngradeRequest_DowngradeAction_value = map[string]int32{
+ "VALIDATE": 0,
+ "ENABLE": 1,
+ "CANCEL": 2,
+ }
+)
-var DowngradeRequest_DowngradeAction_value = map[string]int32{
- "VALIDATE": 0,
- "ENABLE": 1,
- "CANCEL": 2,
+func (x DowngradeRequest_DowngradeAction) Enum() *DowngradeRequest_DowngradeAction {
+ p := new(DowngradeRequest_DowngradeAction)
+ *p = x
+ return p
}
func (x DowngradeRequest_DowngradeAction) String() string {
- return proto.EnumName(DowngradeRequest_DowngradeAction_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DowngradeRequest_DowngradeAction) Descriptor() protoreflect.EnumDescriptor {
+ return file_rpc_proto_enumTypes[7].Descriptor()
+}
+
+func (DowngradeRequest_DowngradeAction) Type() protoreflect.EnumType {
+ return &file_rpc_proto_enumTypes[7]
+}
+
+func (x DowngradeRequest_DowngradeAction) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
+// Deprecated: Use DowngradeRequest_DowngradeAction.Descriptor instead.
func (DowngradeRequest_DowngradeAction) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{57, 0}
+ return file_rpc_proto_rawDescGZIP(), []int{57, 0}
}
type ResponseHeader struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// cluster_id is the ID of the cluster which sent the response.
ClusterId uint64 `protobuf:"varint,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
// member_id is the ID of the member which sent the response.
@@ -283,74 +446,71 @@ type ResponseHeader struct {
// header.revision number.
Revision int64 `protobuf:"varint,3,opt,name=revision,proto3" json:"revision,omitempty"`
// raft_term is the raft term when the request was applied.
- RaftTerm uint64 `protobuf:"varint,4,opt,name=raft_term,json=raftTerm,proto3" json:"raft_term,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ RaftTerm uint64 `protobuf:"varint,4,opt,name=raft_term,json=raftTerm,proto3" json:"raft_term,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ResponseHeader) Reset() { *m = ResponseHeader{} }
-func (m *ResponseHeader) String() string { return proto.CompactTextString(m) }
-func (*ResponseHeader) ProtoMessage() {}
-func (*ResponseHeader) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{0}
-}
-func (m *ResponseHeader) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *ResponseHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_ResponseHeader.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *ResponseHeader) Reset() {
+ *x = ResponseHeader{}
+ mi := &file_rpc_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *ResponseHeader) XXX_Merge(src proto.Message) {
- xxx_messageInfo_ResponseHeader.Merge(m, src)
-}
-func (m *ResponseHeader) XXX_Size() int {
- return m.Size()
+
+func (x *ResponseHeader) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *ResponseHeader) XXX_DiscardUnknown() {
- xxx_messageInfo_ResponseHeader.DiscardUnknown(m)
+
+func (*ResponseHeader) ProtoMessage() {}
+
+func (x *ResponseHeader) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_ResponseHeader proto.InternalMessageInfo
+// Deprecated: Use ResponseHeader.ProtoReflect.Descriptor instead.
+func (*ResponseHeader) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{0}
+}
-func (m *ResponseHeader) GetClusterId() uint64 {
- if m != nil {
- return m.ClusterId
+func (x *ResponseHeader) GetClusterId() uint64 {
+ if x != nil {
+ return x.ClusterId
}
return 0
}
-func (m *ResponseHeader) GetMemberId() uint64 {
- if m != nil {
- return m.MemberId
+func (x *ResponseHeader) GetMemberId() uint64 {
+ if x != nil {
+ return x.MemberId
}
return 0
}
-func (m *ResponseHeader) GetRevision() int64 {
- if m != nil {
- return m.Revision
+func (x *ResponseHeader) GetRevision() int64 {
+ if x != nil {
+ return x.Revision
}
return 0
}
-func (m *ResponseHeader) GetRaftTerm() uint64 {
- if m != nil {
- return m.RaftTerm
+func (x *ResponseHeader) GetRaftTerm() uint64 {
+ if x != nil {
+ return x.RaftTerm
}
return 0
}
type RangeRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// key is the first key for the range. If range_end is not given, the request only looks up key.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the upper bound on the requested range [key, range_end).
@@ -392,138 +552,135 @@ type RangeRequest struct {
MinCreateRevision int64 `protobuf:"varint,12,opt,name=min_create_revision,json=minCreateRevision,proto3" json:"min_create_revision,omitempty"`
// max_create_revision is the upper bound for returned key create revisions; all keys with
// greater create revisions will be filtered away.
- MaxCreateRevision int64 `protobuf:"varint,13,opt,name=max_create_revision,json=maxCreateRevision,proto3" json:"max_create_revision,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ MaxCreateRevision int64 `protobuf:"varint,13,opt,name=max_create_revision,json=maxCreateRevision,proto3" json:"max_create_revision,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *RangeRequest) Reset() { *m = RangeRequest{} }
-func (m *RangeRequest) String() string { return proto.CompactTextString(m) }
-func (*RangeRequest) ProtoMessage() {}
-func (*RangeRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{1}
-}
-func (m *RangeRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *RangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_RangeRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *RangeRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_RangeRequest.Merge(m, src)
+func (x *RangeRequest) Reset() {
+ *x = RangeRequest{}
+ mi := &file_rpc_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *RangeRequest) XXX_Size() int {
- return m.Size()
+
+func (x *RangeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *RangeRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_RangeRequest.DiscardUnknown(m)
+
+func (*RangeRequest) ProtoMessage() {}
+
+func (x *RangeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_RangeRequest proto.InternalMessageInfo
+// Deprecated: Use RangeRequest.ProtoReflect.Descriptor instead.
+func (*RangeRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{1}
+}
-func (m *RangeRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *RangeRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
return nil
}
-func (m *RangeRequest) GetRangeEnd() []byte {
- if m != nil {
- return m.RangeEnd
+func (x *RangeRequest) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
return nil
}
-func (m *RangeRequest) GetLimit() int64 {
- if m != nil {
- return m.Limit
+func (x *RangeRequest) GetLimit() int64 {
+ if x != nil {
+ return x.Limit
}
return 0
}
-func (m *RangeRequest) GetRevision() int64 {
- if m != nil {
- return m.Revision
+func (x *RangeRequest) GetRevision() int64 {
+ if x != nil {
+ return x.Revision
}
return 0
}
-func (m *RangeRequest) GetSortOrder() RangeRequest_SortOrder {
- if m != nil {
- return m.SortOrder
+func (x *RangeRequest) GetSortOrder() RangeRequest_SortOrder {
+ if x != nil {
+ return x.SortOrder
}
return RangeRequest_NONE
}
-func (m *RangeRequest) GetSortTarget() RangeRequest_SortTarget {
- if m != nil {
- return m.SortTarget
+func (x *RangeRequest) GetSortTarget() RangeRequest_SortTarget {
+ if x != nil {
+ return x.SortTarget
}
return RangeRequest_KEY
}
-func (m *RangeRequest) GetSerializable() bool {
- if m != nil {
- return m.Serializable
+func (x *RangeRequest) GetSerializable() bool {
+ if x != nil {
+ return x.Serializable
}
return false
}
-func (m *RangeRequest) GetKeysOnly() bool {
- if m != nil {
- return m.KeysOnly
+func (x *RangeRequest) GetKeysOnly() bool {
+ if x != nil {
+ return x.KeysOnly
}
return false
}
-func (m *RangeRequest) GetCountOnly() bool {
- if m != nil {
- return m.CountOnly
+func (x *RangeRequest) GetCountOnly() bool {
+ if x != nil {
+ return x.CountOnly
}
return false
}
-func (m *RangeRequest) GetMinModRevision() int64 {
- if m != nil {
- return m.MinModRevision
+func (x *RangeRequest) GetMinModRevision() int64 {
+ if x != nil {
+ return x.MinModRevision
}
return 0
}
-func (m *RangeRequest) GetMaxModRevision() int64 {
- if m != nil {
- return m.MaxModRevision
+func (x *RangeRequest) GetMaxModRevision() int64 {
+ if x != nil {
+ return x.MaxModRevision
}
return 0
}
-func (m *RangeRequest) GetMinCreateRevision() int64 {
- if m != nil {
- return m.MinCreateRevision
+func (x *RangeRequest) GetMinCreateRevision() int64 {
+ if x != nil {
+ return x.MinCreateRevision
}
return 0
}
-func (m *RangeRequest) GetMaxCreateRevision() int64 {
- if m != nil {
- return m.MaxCreateRevision
+func (x *RangeRequest) GetMaxCreateRevision() int64 {
+ if x != nil {
+ return x.MaxCreateRevision
}
return 0
}
type RangeResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// kvs is the list of key-value pairs matched by the range request.
// kvs is empty when count is requested.
Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs,proto3" json:"kvs,omitempty"`
@@ -532,74 +689,71 @@ type RangeResponse struct {
// count is set to the actual number of keys within the range when requested.
// Unlike Kvs, it is unaffected by limits and filters (e.g., Min/Max, Create/Modify, Revisions)
// and reflects the full count within the specified range.
- Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *RangeResponse) Reset() { *m = RangeResponse{} }
-func (m *RangeResponse) String() string { return proto.CompactTextString(m) }
-func (*RangeResponse) ProtoMessage() {}
-func (*RangeResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{2}
-}
-func (m *RangeResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *RangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_RangeResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *RangeResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_RangeResponse.Merge(m, src)
+func (x *RangeResponse) Reset() {
+ *x = RangeResponse{}
+ mi := &file_rpc_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *RangeResponse) XXX_Size() int {
- return m.Size()
+
+func (x *RangeResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *RangeResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_RangeResponse.DiscardUnknown(m)
+
+func (*RangeResponse) ProtoMessage() {}
+
+func (x *RangeResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_RangeResponse proto.InternalMessageInfo
+// Deprecated: Use RangeResponse.ProtoReflect.Descriptor instead.
+func (*RangeResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{2}
+}
-func (m *RangeResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *RangeResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *RangeResponse) GetKvs() []*mvccpb.KeyValue {
- if m != nil {
- return m.Kvs
+func (x *RangeResponse) GetKvs() []*mvccpb.KeyValue {
+ if x != nil {
+ return x.Kvs
}
return nil
}
-func (m *RangeResponse) GetMore() bool {
- if m != nil {
- return m.More
+func (x *RangeResponse) GetMore() bool {
+ if x != nil {
+ return x.More
}
return false
}
-func (m *RangeResponse) GetCount() int64 {
- if m != nil {
- return m.Count
+func (x *RangeResponse) GetCount() int64 {
+ if x != nil {
+ return x.Count
}
return 0
}
type PutRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// key is the key, in bytes, to put into the key-value store.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// value is the value, in bytes, to associate with the key in the key-value store.
@@ -615,144 +769,138 @@ type PutRequest struct {
IgnoreValue bool `protobuf:"varint,5,opt,name=ignore_value,json=ignoreValue,proto3" json:"ignore_value,omitempty"`
// If ignore_lease is set, etcd updates the key using its current lease.
// Returns an error if the key does not exist.
- IgnoreLease bool `protobuf:"varint,6,opt,name=ignore_lease,json=ignoreLease,proto3" json:"ignore_lease,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ IgnoreLease bool `protobuf:"varint,6,opt,name=ignore_lease,json=ignoreLease,proto3" json:"ignore_lease,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *PutRequest) Reset() { *m = PutRequest{} }
-func (m *PutRequest) String() string { return proto.CompactTextString(m) }
-func (*PutRequest) ProtoMessage() {}
-func (*PutRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{3}
-}
-func (m *PutRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *PutRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_PutRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *PutRequest) Reset() {
+ *x = PutRequest{}
+ mi := &file_rpc_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *PutRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_PutRequest.Merge(m, src)
-}
-func (m *PutRequest) XXX_Size() int {
- return m.Size()
+
+func (x *PutRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *PutRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_PutRequest.DiscardUnknown(m)
+
+func (*PutRequest) ProtoMessage() {}
+
+func (x *PutRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_PutRequest proto.InternalMessageInfo
+// Deprecated: Use PutRequest.ProtoReflect.Descriptor instead.
+func (*PutRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{3}
+}
-func (m *PutRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *PutRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
return nil
}
-func (m *PutRequest) GetValue() []byte {
- if m != nil {
- return m.Value
+func (x *PutRequest) GetValue() []byte {
+ if x != nil {
+ return x.Value
}
return nil
}
-func (m *PutRequest) GetLease() int64 {
- if m != nil {
- return m.Lease
+func (x *PutRequest) GetLease() int64 {
+ if x != nil {
+ return x.Lease
}
return 0
}
-func (m *PutRequest) GetPrevKv() bool {
- if m != nil {
- return m.PrevKv
+func (x *PutRequest) GetPrevKv() bool {
+ if x != nil {
+ return x.PrevKv
}
return false
}
-func (m *PutRequest) GetIgnoreValue() bool {
- if m != nil {
- return m.IgnoreValue
+func (x *PutRequest) GetIgnoreValue() bool {
+ if x != nil {
+ return x.IgnoreValue
}
return false
}
-func (m *PutRequest) GetIgnoreLease() bool {
- if m != nil {
- return m.IgnoreLease
+func (x *PutRequest) GetIgnoreLease() bool {
+ if x != nil {
+ return x.IgnoreLease
}
return false
}
type PutResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// if prev_kv is set in the request, the previous key-value pair will be returned.
- PrevKv *mvccpb.KeyValue `protobuf:"bytes,2,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ PrevKv *mvccpb.KeyValue `protobuf:"bytes,2,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *PutResponse) Reset() { *m = PutResponse{} }
-func (m *PutResponse) String() string { return proto.CompactTextString(m) }
-func (*PutResponse) ProtoMessage() {}
-func (*PutResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{4}
-}
-func (m *PutResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *PutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_PutResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *PutResponse) Reset() {
+ *x = PutResponse{}
+ mi := &file_rpc_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *PutResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_PutResponse.Merge(m, src)
-}
-func (m *PutResponse) XXX_Size() int {
- return m.Size()
+
+func (x *PutResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *PutResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_PutResponse.DiscardUnknown(m)
+
+func (*PutResponse) ProtoMessage() {}
+
+func (x *PutResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_PutResponse proto.InternalMessageInfo
+// Deprecated: Use PutResponse.ProtoReflect.Descriptor instead.
+func (*PutResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{4}
+}
-func (m *PutResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *PutResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *PutResponse) GetPrevKv() *mvccpb.KeyValue {
- if m != nil {
- return m.PrevKv
+func (x *PutResponse) GetPrevKv() *mvccpb.KeyValue {
+ if x != nil {
+ return x.PrevKv
}
return nil
}
type DeleteRangeRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// key is the first key to delete in the range.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the key following the last key to delete for the range [key, range_end).
@@ -763,371 +911,366 @@ type DeleteRangeRequest struct {
RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
// If prev_kv is set, etcd gets the previous key-value pairs before deleting it.
// The previous key-value pairs will be returned in the delete response.
- PrevKv bool `protobuf:"varint,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ PrevKv bool `protobuf:"varint,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DeleteRangeRequest) Reset() { *m = DeleteRangeRequest{} }
-func (m *DeleteRangeRequest) String() string { return proto.CompactTextString(m) }
-func (*DeleteRangeRequest) ProtoMessage() {}
-func (*DeleteRangeRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{5}
-}
-func (m *DeleteRangeRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DeleteRangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DeleteRangeRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *DeleteRangeRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DeleteRangeRequest.Merge(m, src)
+func (x *DeleteRangeRequest) Reset() {
+ *x = DeleteRangeRequest{}
+ mi := &file_rpc_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DeleteRangeRequest) XXX_Size() int {
- return m.Size()
+
+func (x *DeleteRangeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DeleteRangeRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_DeleteRangeRequest.DiscardUnknown(m)
+
+func (*DeleteRangeRequest) ProtoMessage() {}
+
+func (x *DeleteRangeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DeleteRangeRequest proto.InternalMessageInfo
+// Deprecated: Use DeleteRangeRequest.ProtoReflect.Descriptor instead.
+func (*DeleteRangeRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{5}
+}
-func (m *DeleteRangeRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *DeleteRangeRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
return nil
}
-func (m *DeleteRangeRequest) GetRangeEnd() []byte {
- if m != nil {
- return m.RangeEnd
+func (x *DeleteRangeRequest) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
return nil
}
-func (m *DeleteRangeRequest) GetPrevKv() bool {
- if m != nil {
- return m.PrevKv
+func (x *DeleteRangeRequest) GetPrevKv() bool {
+ if x != nil {
+ return x.PrevKv
}
return false
}
type DeleteRangeResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// deleted is the number of keys deleted by the delete range request.
Deleted int64 `protobuf:"varint,2,opt,name=deleted,proto3" json:"deleted,omitempty"`
// if prev_kv is set in the request, the previous key-value pairs will be returned.
- PrevKvs []*mvccpb.KeyValue `protobuf:"bytes,3,rep,name=prev_kvs,json=prevKvs,proto3" json:"prev_kvs,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ PrevKvs []*mvccpb.KeyValue `protobuf:"bytes,3,rep,name=prev_kvs,json=prevKvs,proto3" json:"prev_kvs,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DeleteRangeResponse) Reset() { *m = DeleteRangeResponse{} }
-func (m *DeleteRangeResponse) String() string { return proto.CompactTextString(m) }
-func (*DeleteRangeResponse) ProtoMessage() {}
-func (*DeleteRangeResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{6}
-}
-func (m *DeleteRangeResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DeleteRangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DeleteRangeResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *DeleteRangeResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DeleteRangeResponse.Merge(m, src)
+func (x *DeleteRangeResponse) Reset() {
+ *x = DeleteRangeResponse{}
+ mi := &file_rpc_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DeleteRangeResponse) XXX_Size() int {
- return m.Size()
+
+func (x *DeleteRangeResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DeleteRangeResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_DeleteRangeResponse.DiscardUnknown(m)
+
+func (*DeleteRangeResponse) ProtoMessage() {}
+
+func (x *DeleteRangeResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DeleteRangeResponse proto.InternalMessageInfo
+// Deprecated: Use DeleteRangeResponse.ProtoReflect.Descriptor instead.
+func (*DeleteRangeResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{6}
+}
-func (m *DeleteRangeResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *DeleteRangeResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *DeleteRangeResponse) GetDeleted() int64 {
- if m != nil {
- return m.Deleted
+func (x *DeleteRangeResponse) GetDeleted() int64 {
+ if x != nil {
+ return x.Deleted
}
return 0
}
-func (m *DeleteRangeResponse) GetPrevKvs() []*mvccpb.KeyValue {
- if m != nil {
- return m.PrevKvs
+func (x *DeleteRangeResponse) GetPrevKvs() []*mvccpb.KeyValue {
+ if x != nil {
+ return x.PrevKvs
}
return nil
}
type RequestOp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// request is a union of request types accepted by a transaction.
//
// Types that are valid to be assigned to Request:
+ //
// *RequestOp_RequestRange
// *RequestOp_RequestPut
// *RequestOp_RequestDeleteRange
// *RequestOp_RequestTxn
- Request isRequestOp_Request `protobuf_oneof:"request"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Request isRequestOp_Request `protobuf_oneof:"request"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *RequestOp) Reset() { *m = RequestOp{} }
-func (m *RequestOp) String() string { return proto.CompactTextString(m) }
-func (*RequestOp) ProtoMessage() {}
-func (*RequestOp) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{7}
-}
-func (m *RequestOp) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *RequestOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_RequestOp.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *RequestOp) XXX_Merge(src proto.Message) {
- xxx_messageInfo_RequestOp.Merge(m, src)
+func (x *RequestOp) Reset() {
+ *x = RequestOp{}
+ mi := &file_rpc_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *RequestOp) XXX_Size() int {
- return m.Size()
-}
-func (m *RequestOp) XXX_DiscardUnknown() {
- xxx_messageInfo_RequestOp.DiscardUnknown(m)
+
+func (x *RequestOp) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_RequestOp proto.InternalMessageInfo
+func (*RequestOp) ProtoMessage() {}
-type isRequestOp_Request interface {
- isRequestOp_Request()
- MarshalTo([]byte) (int, error)
- Size() int
+func (x *RequestOp) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[7]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-type RequestOp_RequestRange struct {
- RequestRange *RangeRequest `protobuf:"bytes,1,opt,name=request_range,json=requestRange,proto3,oneof" json:"request_range,omitempty"`
-}
-type RequestOp_RequestPut struct {
- RequestPut *PutRequest `protobuf:"bytes,2,opt,name=request_put,json=requestPut,proto3,oneof" json:"request_put,omitempty"`
-}
-type RequestOp_RequestDeleteRange struct {
- RequestDeleteRange *DeleteRangeRequest `protobuf:"bytes,3,opt,name=request_delete_range,json=requestDeleteRange,proto3,oneof" json:"request_delete_range,omitempty"`
-}
-type RequestOp_RequestTxn struct {
- RequestTxn *TxnRequest `protobuf:"bytes,4,opt,name=request_txn,json=requestTxn,proto3,oneof" json:"request_txn,omitempty"`
+// Deprecated: Use RequestOp.ProtoReflect.Descriptor instead.
+func (*RequestOp) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{7}
}
-func (*RequestOp_RequestRange) isRequestOp_Request() {}
-func (*RequestOp_RequestPut) isRequestOp_Request() {}
-func (*RequestOp_RequestDeleteRange) isRequestOp_Request() {}
-func (*RequestOp_RequestTxn) isRequestOp_Request() {}
-
-func (m *RequestOp) GetRequest() isRequestOp_Request {
- if m != nil {
- return m.Request
+func (x *RequestOp) GetRequest() isRequestOp_Request {
+ if x != nil {
+ return x.Request
}
return nil
}
-func (m *RequestOp) GetRequestRange() *RangeRequest {
- if x, ok := m.GetRequest().(*RequestOp_RequestRange); ok {
- return x.RequestRange
+func (x *RequestOp) GetRequestRange() *RangeRequest {
+ if x != nil {
+ if x, ok := x.Request.(*RequestOp_RequestRange); ok {
+ return x.RequestRange
+ }
}
return nil
}
-func (m *RequestOp) GetRequestPut() *PutRequest {
- if x, ok := m.GetRequest().(*RequestOp_RequestPut); ok {
- return x.RequestPut
+func (x *RequestOp) GetRequestPut() *PutRequest {
+ if x != nil {
+ if x, ok := x.Request.(*RequestOp_RequestPut); ok {
+ return x.RequestPut
+ }
}
return nil
}
-func (m *RequestOp) GetRequestDeleteRange() *DeleteRangeRequest {
- if x, ok := m.GetRequest().(*RequestOp_RequestDeleteRange); ok {
- return x.RequestDeleteRange
+func (x *RequestOp) GetRequestDeleteRange() *DeleteRangeRequest {
+ if x != nil {
+ if x, ok := x.Request.(*RequestOp_RequestDeleteRange); ok {
+ return x.RequestDeleteRange
+ }
}
return nil
}
-func (m *RequestOp) GetRequestTxn() *TxnRequest {
- if x, ok := m.GetRequest().(*RequestOp_RequestTxn); ok {
- return x.RequestTxn
+func (x *RequestOp) GetRequestTxn() *TxnRequest {
+ if x != nil {
+ if x, ok := x.Request.(*RequestOp_RequestTxn); ok {
+ return x.RequestTxn
+ }
}
return nil
}
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*RequestOp) XXX_OneofWrappers() []interface{} {
- return []interface{}{
- (*RequestOp_RequestRange)(nil),
- (*RequestOp_RequestPut)(nil),
- (*RequestOp_RequestDeleteRange)(nil),
- (*RequestOp_RequestTxn)(nil),
- }
+type isRequestOp_Request interface {
+ isRequestOp_Request()
+}
+
+type RequestOp_RequestRange struct {
+ RequestRange *RangeRequest `protobuf:"bytes,1,opt,name=request_range,json=requestRange,proto3,oneof"`
+}
+
+type RequestOp_RequestPut struct {
+ RequestPut *PutRequest `protobuf:"bytes,2,opt,name=request_put,json=requestPut,proto3,oneof"`
+}
+
+type RequestOp_RequestDeleteRange struct {
+ RequestDeleteRange *DeleteRangeRequest `protobuf:"bytes,3,opt,name=request_delete_range,json=requestDeleteRange,proto3,oneof"`
}
+type RequestOp_RequestTxn struct {
+ RequestTxn *TxnRequest `protobuf:"bytes,4,opt,name=request_txn,json=requestTxn,proto3,oneof"`
+}
+
+func (*RequestOp_RequestRange) isRequestOp_Request() {}
+
+func (*RequestOp_RequestPut) isRequestOp_Request() {}
+
+func (*RequestOp_RequestDeleteRange) isRequestOp_Request() {}
+
+func (*RequestOp_RequestTxn) isRequestOp_Request() {}
+
type ResponseOp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// response is a union of response types returned by a transaction.
//
// Types that are valid to be assigned to Response:
+ //
// *ResponseOp_ResponseRange
// *ResponseOp_ResponsePut
// *ResponseOp_ResponseDeleteRange
// *ResponseOp_ResponseTxn
- Response isResponseOp_Response `protobuf_oneof:"response"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Response isResponseOp_Response `protobuf_oneof:"response"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ResponseOp) Reset() { *m = ResponseOp{} }
-func (m *ResponseOp) String() string { return proto.CompactTextString(m) }
-func (*ResponseOp) ProtoMessage() {}
-func (*ResponseOp) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{8}
-}
-func (m *ResponseOp) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *ResponseOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_ResponseOp.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *ResponseOp) Reset() {
+ *x = ResponseOp{}
+ mi := &file_rpc_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *ResponseOp) XXX_Merge(src proto.Message) {
- xxx_messageInfo_ResponseOp.Merge(m, src)
-}
-func (m *ResponseOp) XXX_Size() int {
- return m.Size()
-}
-func (m *ResponseOp) XXX_DiscardUnknown() {
- xxx_messageInfo_ResponseOp.DiscardUnknown(m)
+
+func (x *ResponseOp) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_ResponseOp proto.InternalMessageInfo
+func (*ResponseOp) ProtoMessage() {}
-type isResponseOp_Response interface {
- isResponseOp_Response()
- MarshalTo([]byte) (int, error)
- Size() int
+func (x *ResponseOp) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[8]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-type ResponseOp_ResponseRange struct {
- ResponseRange *RangeResponse `protobuf:"bytes,1,opt,name=response_range,json=responseRange,proto3,oneof" json:"response_range,omitempty"`
-}
-type ResponseOp_ResponsePut struct {
- ResponsePut *PutResponse `protobuf:"bytes,2,opt,name=response_put,json=responsePut,proto3,oneof" json:"response_put,omitempty"`
-}
-type ResponseOp_ResponseDeleteRange struct {
- ResponseDeleteRange *DeleteRangeResponse `protobuf:"bytes,3,opt,name=response_delete_range,json=responseDeleteRange,proto3,oneof" json:"response_delete_range,omitempty"`
-}
-type ResponseOp_ResponseTxn struct {
- ResponseTxn *TxnResponse `protobuf:"bytes,4,opt,name=response_txn,json=responseTxn,proto3,oneof" json:"response_txn,omitempty"`
+// Deprecated: Use ResponseOp.ProtoReflect.Descriptor instead.
+func (*ResponseOp) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{8}
}
-func (*ResponseOp_ResponseRange) isResponseOp_Response() {}
-func (*ResponseOp_ResponsePut) isResponseOp_Response() {}
-func (*ResponseOp_ResponseDeleteRange) isResponseOp_Response() {}
-func (*ResponseOp_ResponseTxn) isResponseOp_Response() {}
-
-func (m *ResponseOp) GetResponse() isResponseOp_Response {
- if m != nil {
- return m.Response
+func (x *ResponseOp) GetResponse() isResponseOp_Response {
+ if x != nil {
+ return x.Response
}
return nil
}
-func (m *ResponseOp) GetResponseRange() *RangeResponse {
- if x, ok := m.GetResponse().(*ResponseOp_ResponseRange); ok {
- return x.ResponseRange
+func (x *ResponseOp) GetResponseRange() *RangeResponse {
+ if x != nil {
+ if x, ok := x.Response.(*ResponseOp_ResponseRange); ok {
+ return x.ResponseRange
+ }
}
return nil
}
-func (m *ResponseOp) GetResponsePut() *PutResponse {
- if x, ok := m.GetResponse().(*ResponseOp_ResponsePut); ok {
- return x.ResponsePut
+func (x *ResponseOp) GetResponsePut() *PutResponse {
+ if x != nil {
+ if x, ok := x.Response.(*ResponseOp_ResponsePut); ok {
+ return x.ResponsePut
+ }
}
return nil
}
-func (m *ResponseOp) GetResponseDeleteRange() *DeleteRangeResponse {
- if x, ok := m.GetResponse().(*ResponseOp_ResponseDeleteRange); ok {
- return x.ResponseDeleteRange
+func (x *ResponseOp) GetResponseDeleteRange() *DeleteRangeResponse {
+ if x != nil {
+ if x, ok := x.Response.(*ResponseOp_ResponseDeleteRange); ok {
+ return x.ResponseDeleteRange
+ }
}
return nil
}
-func (m *ResponseOp) GetResponseTxn() *TxnResponse {
- if x, ok := m.GetResponse().(*ResponseOp_ResponseTxn); ok {
- return x.ResponseTxn
+func (x *ResponseOp) GetResponseTxn() *TxnResponse {
+ if x != nil {
+ if x, ok := x.Response.(*ResponseOp_ResponseTxn); ok {
+ return x.ResponseTxn
+ }
}
return nil
}
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*ResponseOp) XXX_OneofWrappers() []interface{} {
- return []interface{}{
- (*ResponseOp_ResponseRange)(nil),
- (*ResponseOp_ResponsePut)(nil),
- (*ResponseOp_ResponseDeleteRange)(nil),
- (*ResponseOp_ResponseTxn)(nil),
- }
+type isResponseOp_Response interface {
+ isResponseOp_Response()
}
-type Compare struct {
- // result is logical comparison operation for this comparison.
- Result Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult" json:"result,omitempty"`
- // target is the key-value field to inspect for the comparison.
+type ResponseOp_ResponseRange struct {
+ ResponseRange *RangeResponse `protobuf:"bytes,1,opt,name=response_range,json=responseRange,proto3,oneof"`
+}
+
+type ResponseOp_ResponsePut struct {
+ ResponsePut *PutResponse `protobuf:"bytes,2,opt,name=response_put,json=responsePut,proto3,oneof"`
+}
+
+type ResponseOp_ResponseDeleteRange struct {
+ ResponseDeleteRange *DeleteRangeResponse `protobuf:"bytes,3,opt,name=response_delete_range,json=responseDeleteRange,proto3,oneof"`
+}
+
+type ResponseOp_ResponseTxn struct {
+ ResponseTxn *TxnResponse `protobuf:"bytes,4,opt,name=response_txn,json=responseTxn,proto3,oneof"`
+}
+
+func (*ResponseOp_ResponseRange) isResponseOp_Response() {}
+
+func (*ResponseOp_ResponsePut) isResponseOp_Response() {}
+
+func (*ResponseOp_ResponseDeleteRange) isResponseOp_Response() {}
+
+func (*ResponseOp_ResponseTxn) isResponseOp_Response() {}
+
+type Compare struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // result is logical comparison operation for this comparison.
+ Result Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult" json:"result,omitempty"`
+ // target is the key-value field to inspect for the comparison.
Target Compare_CompareTarget `protobuf:"varint,2,opt,name=target,proto3,enum=etcdserverpb.Compare_CompareTarget" json:"target,omitempty"`
// key is the subject key for the comparison operation.
Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
// Types that are valid to be assigned to TargetUnion:
+ //
// *Compare_Version
// *Compare_CreateRevision
// *Compare_ModRevision
@@ -1136,154 +1279,160 @@ type Compare struct {
TargetUnion isCompare_TargetUnion `protobuf_oneof:"target_union"`
// range_end compares the given target to all keys in the range [key, range_end).
// See RangeRequest for more details on key ranges.
- RangeEnd []byte `protobuf:"bytes,64,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ RangeEnd []byte `protobuf:"bytes,64,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"` // TODO: fill out with most of the rest of RangeRequest fields when needed.
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Compare) Reset() { *m = Compare{} }
-func (m *Compare) String() string { return proto.CompactTextString(m) }
-func (*Compare) ProtoMessage() {}
-func (*Compare) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{9}
-}
-func (m *Compare) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Compare) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Compare.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Compare) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Compare.Merge(m, src)
+func (x *Compare) Reset() {
+ *x = Compare{}
+ mi := &file_rpc_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Compare) XXX_Size() int {
- return m.Size()
-}
-func (m *Compare) XXX_DiscardUnknown() {
- xxx_messageInfo_Compare.DiscardUnknown(m)
+
+func (x *Compare) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_Compare proto.InternalMessageInfo
+func (*Compare) ProtoMessage() {}
-type isCompare_TargetUnion interface {
- isCompare_TargetUnion()
- MarshalTo([]byte) (int, error)
- Size() int
+func (x *Compare) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[9]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-type Compare_Version struct {
- Version int64 `protobuf:"varint,4,opt,name=version,proto3,oneof" json:"version,omitempty"`
-}
-type Compare_CreateRevision struct {
- CreateRevision int64 `protobuf:"varint,5,opt,name=create_revision,json=createRevision,proto3,oneof" json:"create_revision,omitempty"`
-}
-type Compare_ModRevision struct {
- ModRevision int64 `protobuf:"varint,6,opt,name=mod_revision,json=modRevision,proto3,oneof" json:"mod_revision,omitempty"`
-}
-type Compare_Value struct {
- Value []byte `protobuf:"bytes,7,opt,name=value,proto3,oneof" json:"value,omitempty"`
-}
-type Compare_Lease struct {
- Lease int64 `protobuf:"varint,8,opt,name=lease,proto3,oneof" json:"lease,omitempty"`
+// Deprecated: Use Compare.ProtoReflect.Descriptor instead.
+func (*Compare) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{9}
}
-func (*Compare_Version) isCompare_TargetUnion() {}
-func (*Compare_CreateRevision) isCompare_TargetUnion() {}
-func (*Compare_ModRevision) isCompare_TargetUnion() {}
-func (*Compare_Value) isCompare_TargetUnion() {}
-func (*Compare_Lease) isCompare_TargetUnion() {}
-
-func (m *Compare) GetTargetUnion() isCompare_TargetUnion {
- if m != nil {
- return m.TargetUnion
+func (x *Compare) GetResult() Compare_CompareResult {
+ if x != nil {
+ return x.Result
}
- return nil
+ return Compare_EQUAL
}
-func (m *Compare) GetResult() Compare_CompareResult {
- if m != nil {
- return m.Result
+func (x *Compare) GetTarget() Compare_CompareTarget {
+ if x != nil {
+ return x.Target
}
- return Compare_EQUAL
+ return Compare_VERSION
}
-func (m *Compare) GetTarget() Compare_CompareTarget {
- if m != nil {
- return m.Target
+func (x *Compare) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
- return Compare_VERSION
+ return nil
}
-func (m *Compare) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *Compare) GetTargetUnion() isCompare_TargetUnion {
+ if x != nil {
+ return x.TargetUnion
}
return nil
}
-func (m *Compare) GetVersion() int64 {
- if x, ok := m.GetTargetUnion().(*Compare_Version); ok {
- return x.Version
+func (x *Compare) GetVersion() int64 {
+ if x != nil {
+ if x, ok := x.TargetUnion.(*Compare_Version); ok {
+ return x.Version
+ }
}
return 0
}
-func (m *Compare) GetCreateRevision() int64 {
- if x, ok := m.GetTargetUnion().(*Compare_CreateRevision); ok {
- return x.CreateRevision
+func (x *Compare) GetCreateRevision() int64 {
+ if x != nil {
+ if x, ok := x.TargetUnion.(*Compare_CreateRevision); ok {
+ return x.CreateRevision
+ }
}
return 0
}
-func (m *Compare) GetModRevision() int64 {
- if x, ok := m.GetTargetUnion().(*Compare_ModRevision); ok {
- return x.ModRevision
+func (x *Compare) GetModRevision() int64 {
+ if x != nil {
+ if x, ok := x.TargetUnion.(*Compare_ModRevision); ok {
+ return x.ModRevision
+ }
}
return 0
}
-func (m *Compare) GetValue() []byte {
- if x, ok := m.GetTargetUnion().(*Compare_Value); ok {
- return x.Value
+func (x *Compare) GetValue() []byte {
+ if x != nil {
+ if x, ok := x.TargetUnion.(*Compare_Value); ok {
+ return x.Value
+ }
}
return nil
}
-func (m *Compare) GetLease() int64 {
- if x, ok := m.GetTargetUnion().(*Compare_Lease); ok {
- return x.Lease
+func (x *Compare) GetLease() int64 {
+ if x != nil {
+ if x, ok := x.TargetUnion.(*Compare_Lease); ok {
+ return x.Lease
+ }
}
return 0
}
-func (m *Compare) GetRangeEnd() []byte {
- if m != nil {
- return m.RangeEnd
+func (x *Compare) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
return nil
}
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*Compare) XXX_OneofWrappers() []interface{} {
- return []interface{}{
- (*Compare_Version)(nil),
- (*Compare_CreateRevision)(nil),
- (*Compare_ModRevision)(nil),
- (*Compare_Value)(nil),
- (*Compare_Lease)(nil),
- }
+type isCompare_TargetUnion interface {
+ isCompare_TargetUnion()
+}
+
+type Compare_Version struct {
+ // version is the version of the given key
+ Version int64 `protobuf:"varint,4,opt,name=version,proto3,oneof"`
+}
+
+type Compare_CreateRevision struct {
+ // create_revision is the creation revision of the given key
+ CreateRevision int64 `protobuf:"varint,5,opt,name=create_revision,json=createRevision,proto3,oneof"`
+}
+
+type Compare_ModRevision struct {
+ // mod_revision is the last modified revision of the given key.
+ ModRevision int64 `protobuf:"varint,6,opt,name=mod_revision,json=modRevision,proto3,oneof"`
+}
+
+type Compare_Value struct {
+ // value is the value of the given key, in bytes.
+ Value []byte `protobuf:"bytes,7,opt,name=value,proto3,oneof"`
+}
+
+type Compare_Lease struct {
+ // lease is the lease id of the given key.
+ Lease int64 `protobuf:"varint,8,opt,name=lease,proto3,oneof"` // leave room for more target_union field tags, jump to 64
}
+func (*Compare_Version) isCompare_TargetUnion() {}
+
+func (*Compare_CreateRevision) isCompare_TargetUnion() {}
+
+func (*Compare_ModRevision) isCompare_TargetUnion() {}
+
+func (*Compare_Value) isCompare_TargetUnion() {}
+
+func (*Compare_Lease) isCompare_TargetUnion() {}
+
// From google paxosdb paper:
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
@@ -1300,6 +1449,7 @@ func (*Compare) XXX_OneofWrappers() []interface{} {
// true.
// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
type TxnRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// compare is a list of predicates representing a conjunction of terms.
// If the comparisons succeed, then the success requests will be processed in order,
// and the response will contain their respective responses in order.
@@ -1309,128 +1459,121 @@ type TxnRequest struct {
// success is a list of requests which will be applied when compare evaluates to true.
Success []*RequestOp `protobuf:"bytes,2,rep,name=success,proto3" json:"success,omitempty"`
// failure is a list of requests which will be applied when compare evaluates to false.
- Failure []*RequestOp `protobuf:"bytes,3,rep,name=failure,proto3" json:"failure,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Failure []*RequestOp `protobuf:"bytes,3,rep,name=failure,proto3" json:"failure,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *TxnRequest) Reset() { *m = TxnRequest{} }
-func (m *TxnRequest) String() string { return proto.CompactTextString(m) }
-func (*TxnRequest) ProtoMessage() {}
-func (*TxnRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{10}
-}
-func (m *TxnRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *TxnRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_TxnRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *TxnRequest) Reset() {
+ *x = TxnRequest{}
+ mi := &file_rpc_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *TxnRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_TxnRequest.Merge(m, src)
-}
-func (m *TxnRequest) XXX_Size() int {
- return m.Size()
+
+func (x *TxnRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *TxnRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_TxnRequest.DiscardUnknown(m)
+
+func (*TxnRequest) ProtoMessage() {}
+
+func (x *TxnRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[10]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_TxnRequest proto.InternalMessageInfo
+// Deprecated: Use TxnRequest.ProtoReflect.Descriptor instead.
+func (*TxnRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{10}
+}
-func (m *TxnRequest) GetCompare() []*Compare {
- if m != nil {
- return m.Compare
+func (x *TxnRequest) GetCompare() []*Compare {
+ if x != nil {
+ return x.Compare
}
return nil
}
-func (m *TxnRequest) GetSuccess() []*RequestOp {
- if m != nil {
- return m.Success
+func (x *TxnRequest) GetSuccess() []*RequestOp {
+ if x != nil {
+ return x.Success
}
return nil
}
-func (m *TxnRequest) GetFailure() []*RequestOp {
- if m != nil {
- return m.Failure
+func (x *TxnRequest) GetFailure() []*RequestOp {
+ if x != nil {
+ return x.Failure
}
return nil
}
type TxnResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// succeeded is set to true if the compare evaluated to true or false otherwise.
Succeeded bool `protobuf:"varint,2,opt,name=succeeded,proto3" json:"succeeded,omitempty"`
// responses is a list of responses corresponding to the results from applying
// success if succeeded is true or failure if succeeded is false.
- Responses []*ResponseOp `protobuf:"bytes,3,rep,name=responses,proto3" json:"responses,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Responses []*ResponseOp `protobuf:"bytes,3,rep,name=responses,proto3" json:"responses,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *TxnResponse) Reset() { *m = TxnResponse{} }
-func (m *TxnResponse) String() string { return proto.CompactTextString(m) }
-func (*TxnResponse) ProtoMessage() {}
-func (*TxnResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{11}
-}
-func (m *TxnResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *TxnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_TxnResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *TxnResponse) Reset() {
+ *x = TxnResponse{}
+ mi := &file_rpc_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *TxnResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_TxnResponse.Merge(m, src)
-}
-func (m *TxnResponse) XXX_Size() int {
- return m.Size()
+
+func (x *TxnResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *TxnResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_TxnResponse.DiscardUnknown(m)
+
+func (*TxnResponse) ProtoMessage() {}
+
+func (x *TxnResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[11]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_TxnResponse proto.InternalMessageInfo
+// Deprecated: Use TxnResponse.ProtoReflect.Descriptor instead.
+func (*TxnResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{11}
+}
-func (m *TxnResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *TxnResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *TxnResponse) GetSucceeded() bool {
- if m != nil {
- return m.Succeeded
+func (x *TxnResponse) GetSucceeded() bool {
+ if x != nil {
+ return x.Succeeded
}
return false
}
-func (m *TxnResponse) GetResponses() []*ResponseOp {
- if m != nil {
- return m.Responses
+func (x *TxnResponse) GetResponses() []*ResponseOp {
+ if x != nil {
+ return x.Responses
}
return nil
}
@@ -1438,368 +1581,348 @@ func (m *TxnResponse) GetResponses() []*ResponseOp {
// CompactionRequest compacts the key-value store up to a given revision. All superseded keys
// with a revision less than the compaction revision will be removed.
type CompactionRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// revision is the key-value store revision for the compaction operation.
Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
// physical is set so the RPC will wait until the compaction is physically
// applied to the local database such that compacted entries are totally
// removed from the backend database.
- Physical bool `protobuf:"varint,2,opt,name=physical,proto3" json:"physical,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Physical bool `protobuf:"varint,2,opt,name=physical,proto3" json:"physical,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *CompactionRequest) Reset() { *m = CompactionRequest{} }
-func (m *CompactionRequest) String() string { return proto.CompactTextString(m) }
-func (*CompactionRequest) ProtoMessage() {}
-func (*CompactionRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{12}
-}
-func (m *CompactionRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *CompactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_CompactionRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *CompactionRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_CompactionRequest.Merge(m, src)
+func (x *CompactionRequest) Reset() {
+ *x = CompactionRequest{}
+ mi := &file_rpc_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *CompactionRequest) XXX_Size() int {
- return m.Size()
+
+func (x *CompactionRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *CompactionRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_CompactionRequest.DiscardUnknown(m)
+
+func (*CompactionRequest) ProtoMessage() {}
+
+func (x *CompactionRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[12]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_CompactionRequest proto.InternalMessageInfo
+// Deprecated: Use CompactionRequest.ProtoReflect.Descriptor instead.
+func (*CompactionRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{12}
+}
-func (m *CompactionRequest) GetRevision() int64 {
- if m != nil {
- return m.Revision
+func (x *CompactionRequest) GetRevision() int64 {
+ if x != nil {
+ return x.Revision
}
return 0
}
-func (m *CompactionRequest) GetPhysical() bool {
- if m != nil {
- return m.Physical
+func (x *CompactionRequest) GetPhysical() bool {
+ if x != nil {
+ return x.Physical
}
return false
}
type CompactionResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *CompactionResponse) Reset() { *m = CompactionResponse{} }
-func (m *CompactionResponse) String() string { return proto.CompactTextString(m) }
-func (*CompactionResponse) ProtoMessage() {}
-func (*CompactionResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{13}
-}
-func (m *CompactionResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *CompactionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_CompactionResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *CompactionResponse) Reset() {
+ *x = CompactionResponse{}
+ mi := &file_rpc_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *CompactionResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_CompactionResponse.Merge(m, src)
-}
-func (m *CompactionResponse) XXX_Size() int {
- return m.Size()
+
+func (x *CompactionResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *CompactionResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_CompactionResponse.DiscardUnknown(m)
+
+func (*CompactionResponse) ProtoMessage() {}
+
+func (x *CompactionResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[13]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_CompactionResponse proto.InternalMessageInfo
+// Deprecated: Use CompactionResponse.ProtoReflect.Descriptor instead.
+func (*CompactionResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{13}
+}
-func (m *CompactionResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *CompactionResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type HashRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *HashRequest) Reset() { *m = HashRequest{} }
-func (m *HashRequest) String() string { return proto.CompactTextString(m) }
-func (*HashRequest) ProtoMessage() {}
-func (*HashRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{14}
-}
-func (m *HashRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *HashRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_HashRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *HashRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_HashRequest.Merge(m, src)
+func (x *HashRequest) Reset() {
+ *x = HashRequest{}
+ mi := &file_rpc_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *HashRequest) XXX_Size() int {
- return m.Size()
+
+func (x *HashRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *HashRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_HashRequest.DiscardUnknown(m)
+
+func (*HashRequest) ProtoMessage() {}
+
+func (x *HashRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[14]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_HashRequest proto.InternalMessageInfo
+// Deprecated: Use HashRequest.ProtoReflect.Descriptor instead.
+func (*HashRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{14}
+}
type HashKVRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// revision is the key-value store revision for the hash operation.
- Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *HashKVRequest) Reset() { *m = HashKVRequest{} }
-func (m *HashKVRequest) String() string { return proto.CompactTextString(m) }
-func (*HashKVRequest) ProtoMessage() {}
-func (*HashKVRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{15}
-}
-func (m *HashKVRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *HashKVRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_HashKVRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *HashKVRequest) Reset() {
+ *x = HashKVRequest{}
+ mi := &file_rpc_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *HashKVRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_HashKVRequest.Merge(m, src)
-}
-func (m *HashKVRequest) XXX_Size() int {
- return m.Size()
+
+func (x *HashKVRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *HashKVRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_HashKVRequest.DiscardUnknown(m)
+
+func (*HashKVRequest) ProtoMessage() {}
+
+func (x *HashKVRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[15]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_HashKVRequest proto.InternalMessageInfo
+// Deprecated: Use HashKVRequest.ProtoReflect.Descriptor instead.
+func (*HashKVRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{15}
+}
-func (m *HashKVRequest) GetRevision() int64 {
- if m != nil {
- return m.Revision
+func (x *HashKVRequest) GetRevision() int64 {
+ if x != nil {
+ return x.Revision
}
return 0
}
type HashKVResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// hash is the hash value computed from the responding member's MVCC keys up to a given revision.
Hash uint32 `protobuf:"varint,2,opt,name=hash,proto3" json:"hash,omitempty"`
// compact_revision is the compacted revision of key-value store when hash begins.
CompactRevision int64 `protobuf:"varint,3,opt,name=compact_revision,json=compactRevision,proto3" json:"compact_revision,omitempty"`
// hash_revision is the revision up to which the hash is calculated.
- HashRevision int64 `protobuf:"varint,4,opt,name=hash_revision,json=hashRevision,proto3" json:"hash_revision,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ HashRevision int64 `protobuf:"varint,4,opt,name=hash_revision,json=hashRevision,proto3" json:"hash_revision,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *HashKVResponse) Reset() { *m = HashKVResponse{} }
-func (m *HashKVResponse) String() string { return proto.CompactTextString(m) }
-func (*HashKVResponse) ProtoMessage() {}
-func (*HashKVResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{16}
-}
-func (m *HashKVResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *HashKVResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_HashKVResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *HashKVResponse) Reset() {
+ *x = HashKVResponse{}
+ mi := &file_rpc_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *HashKVResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_HashKVResponse.Merge(m, src)
-}
-func (m *HashKVResponse) XXX_Size() int {
- return m.Size()
+
+func (x *HashKVResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *HashKVResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_HashKVResponse.DiscardUnknown(m)
+
+func (*HashKVResponse) ProtoMessage() {}
+
+func (x *HashKVResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[16]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_HashKVResponse proto.InternalMessageInfo
+// Deprecated: Use HashKVResponse.ProtoReflect.Descriptor instead.
+func (*HashKVResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{16}
+}
-func (m *HashKVResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *HashKVResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *HashKVResponse) GetHash() uint32 {
- if m != nil {
- return m.Hash
+func (x *HashKVResponse) GetHash() uint32 {
+ if x != nil {
+ return x.Hash
}
return 0
}
-func (m *HashKVResponse) GetCompactRevision() int64 {
- if m != nil {
- return m.CompactRevision
+func (x *HashKVResponse) GetCompactRevision() int64 {
+ if x != nil {
+ return x.CompactRevision
}
return 0
}
-func (m *HashKVResponse) GetHashRevision() int64 {
- if m != nil {
- return m.HashRevision
+func (x *HashKVResponse) GetHashRevision() int64 {
+ if x != nil {
+ return x.HashRevision
}
return 0
}
type HashResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// hash is the hash value computed from the responding member's KV's backend.
- Hash uint32 `protobuf:"varint,2,opt,name=hash,proto3" json:"hash,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Hash uint32 `protobuf:"varint,2,opt,name=hash,proto3" json:"hash,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *HashResponse) Reset() { *m = HashResponse{} }
-func (m *HashResponse) String() string { return proto.CompactTextString(m) }
-func (*HashResponse) ProtoMessage() {}
-func (*HashResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{17}
-}
-func (m *HashResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *HashResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_HashResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *HashResponse) Reset() {
+ *x = HashResponse{}
+ mi := &file_rpc_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *HashResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_HashResponse.Merge(m, src)
-}
-func (m *HashResponse) XXX_Size() int {
- return m.Size()
+
+func (x *HashResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *HashResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_HashResponse.DiscardUnknown(m)
+
+func (*HashResponse) ProtoMessage() {}
+
+func (x *HashResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[17]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_HashResponse proto.InternalMessageInfo
+// Deprecated: Use HashResponse.ProtoReflect.Descriptor instead.
+func (*HashResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{17}
+}
-func (m *HashResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *HashResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *HashResponse) GetHash() uint32 {
- if m != nil {
- return m.Hash
+func (x *HashResponse) GetHash() uint32 {
+ if x != nil {
+ return x.Hash
}
return 0
}
type SnapshotRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *SnapshotRequest) Reset() { *m = SnapshotRequest{} }
-func (m *SnapshotRequest) String() string { return proto.CompactTextString(m) }
-func (*SnapshotRequest) ProtoMessage() {}
-func (*SnapshotRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{18}
-}
-func (m *SnapshotRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *SnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_SnapshotRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *SnapshotRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_SnapshotRequest.Merge(m, src)
+func (x *SnapshotRequest) Reset() {
+ *x = SnapshotRequest{}
+ mi := &file_rpc_proto_msgTypes[18]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *SnapshotRequest) XXX_Size() int {
- return m.Size()
+
+func (x *SnapshotRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *SnapshotRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_SnapshotRequest.DiscardUnknown(m)
+
+func (*SnapshotRequest) ProtoMessage() {}
+
+func (x *SnapshotRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[18]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_SnapshotRequest proto.InternalMessageInfo
+// Deprecated: Use SnapshotRequest.ProtoReflect.Descriptor instead.
+func (*SnapshotRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{18}
+}
type SnapshotResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// header has the current key-value store information. The first header in the snapshot
// stream indicates the point in time of the snapshot.
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
@@ -1810,177 +1933,171 @@ type SnapshotResponse struct {
// local version of server that created the snapshot.
// In cluster with binaries with different version, each cluster can return different result.
// Informs which etcd server version should be used when restoring the snapshot.
- Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *SnapshotResponse) Reset() { *m = SnapshotResponse{} }
-func (m *SnapshotResponse) String() string { return proto.CompactTextString(m) }
-func (*SnapshotResponse) ProtoMessage() {}
-func (*SnapshotResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{19}
-}
-func (m *SnapshotResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *SnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_SnapshotResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *SnapshotResponse) Reset() {
+ *x = SnapshotResponse{}
+ mi := &file_rpc_proto_msgTypes[19]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *SnapshotResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_SnapshotResponse.Merge(m, src)
-}
-func (m *SnapshotResponse) XXX_Size() int {
- return m.Size()
+
+func (x *SnapshotResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *SnapshotResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_SnapshotResponse.DiscardUnknown(m)
+
+func (*SnapshotResponse) ProtoMessage() {}
+
+func (x *SnapshotResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[19]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_SnapshotResponse proto.InternalMessageInfo
+// Deprecated: Use SnapshotResponse.ProtoReflect.Descriptor instead.
+func (*SnapshotResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{19}
+}
-func (m *SnapshotResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *SnapshotResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *SnapshotResponse) GetRemainingBytes() uint64 {
- if m != nil {
- return m.RemainingBytes
+func (x *SnapshotResponse) GetRemainingBytes() uint64 {
+ if x != nil {
+ return x.RemainingBytes
}
return 0
}
-func (m *SnapshotResponse) GetBlob() []byte {
- if m != nil {
- return m.Blob
+func (x *SnapshotResponse) GetBlob() []byte {
+ if x != nil {
+ return x.Blob
}
return nil
}
-func (m *SnapshotResponse) GetVersion() string {
- if m != nil {
- return m.Version
+func (x *SnapshotResponse) GetVersion() string {
+ if x != nil {
+ return x.Version
}
return ""
}
type WatchRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// request_union is a request to either create a new watcher or cancel an existing watcher.
//
// Types that are valid to be assigned to RequestUnion:
+ //
// *WatchRequest_CreateRequest
// *WatchRequest_CancelRequest
// *WatchRequest_ProgressRequest
- RequestUnion isWatchRequest_RequestUnion `protobuf_oneof:"request_union"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ RequestUnion isWatchRequest_RequestUnion `protobuf_oneof:"request_union"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WatchRequest) Reset() { *m = WatchRequest{} }
-func (m *WatchRequest) String() string { return proto.CompactTextString(m) }
-func (*WatchRequest) ProtoMessage() {}
-func (*WatchRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{20}
-}
-func (m *WatchRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *WatchRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_WatchRequest.Merge(m, src)
+func (x *WatchRequest) Reset() {
+ *x = WatchRequest{}
+ mi := &file_rpc_proto_msgTypes[20]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WatchRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *WatchRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_WatchRequest.DiscardUnknown(m)
+
+func (x *WatchRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_WatchRequest proto.InternalMessageInfo
+func (*WatchRequest) ProtoMessage() {}
-type isWatchRequest_RequestUnion interface {
- isWatchRequest_RequestUnion()
- MarshalTo([]byte) (int, error)
- Size() int
+func (x *WatchRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[20]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-type WatchRequest_CreateRequest struct {
- CreateRequest *WatchCreateRequest `protobuf:"bytes,1,opt,name=create_request,json=createRequest,proto3,oneof" json:"create_request,omitempty"`
-}
-type WatchRequest_CancelRequest struct {
- CancelRequest *WatchCancelRequest `protobuf:"bytes,2,opt,name=cancel_request,json=cancelRequest,proto3,oneof" json:"cancel_request,omitempty"`
-}
-type WatchRequest_ProgressRequest struct {
- ProgressRequest *WatchProgressRequest `protobuf:"bytes,3,opt,name=progress_request,json=progressRequest,proto3,oneof" json:"progress_request,omitempty"`
+// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead.
+func (*WatchRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{20}
}
-func (*WatchRequest_CreateRequest) isWatchRequest_RequestUnion() {}
-func (*WatchRequest_CancelRequest) isWatchRequest_RequestUnion() {}
-func (*WatchRequest_ProgressRequest) isWatchRequest_RequestUnion() {}
-
-func (m *WatchRequest) GetRequestUnion() isWatchRequest_RequestUnion {
- if m != nil {
- return m.RequestUnion
+func (x *WatchRequest) GetRequestUnion() isWatchRequest_RequestUnion {
+ if x != nil {
+ return x.RequestUnion
}
return nil
}
-func (m *WatchRequest) GetCreateRequest() *WatchCreateRequest {
- if x, ok := m.GetRequestUnion().(*WatchRequest_CreateRequest); ok {
- return x.CreateRequest
+func (x *WatchRequest) GetCreateRequest() *WatchCreateRequest {
+ if x != nil {
+ if x, ok := x.RequestUnion.(*WatchRequest_CreateRequest); ok {
+ return x.CreateRequest
+ }
}
return nil
}
-func (m *WatchRequest) GetCancelRequest() *WatchCancelRequest {
- if x, ok := m.GetRequestUnion().(*WatchRequest_CancelRequest); ok {
- return x.CancelRequest
+func (x *WatchRequest) GetCancelRequest() *WatchCancelRequest {
+ if x != nil {
+ if x, ok := x.RequestUnion.(*WatchRequest_CancelRequest); ok {
+ return x.CancelRequest
+ }
}
return nil
}
-func (m *WatchRequest) GetProgressRequest() *WatchProgressRequest {
- if x, ok := m.GetRequestUnion().(*WatchRequest_ProgressRequest); ok {
- return x.ProgressRequest
+func (x *WatchRequest) GetProgressRequest() *WatchProgressRequest {
+ if x != nil {
+ if x, ok := x.RequestUnion.(*WatchRequest_ProgressRequest); ok {
+ return x.ProgressRequest
+ }
}
return nil
}
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*WatchRequest) XXX_OneofWrappers() []interface{} {
- return []interface{}{
- (*WatchRequest_CreateRequest)(nil),
- (*WatchRequest_CancelRequest)(nil),
- (*WatchRequest_ProgressRequest)(nil),
- }
+type isWatchRequest_RequestUnion interface {
+ isWatchRequest_RequestUnion()
+}
+
+type WatchRequest_CreateRequest struct {
+ CreateRequest *WatchCreateRequest `protobuf:"bytes,1,opt,name=create_request,json=createRequest,proto3,oneof"`
}
+type WatchRequest_CancelRequest struct {
+ CancelRequest *WatchCancelRequest `protobuf:"bytes,2,opt,name=cancel_request,json=cancelRequest,proto3,oneof"`
+}
+
+type WatchRequest_ProgressRequest struct {
+ ProgressRequest *WatchProgressRequest `protobuf:"bytes,3,opt,name=progress_request,json=progressRequest,proto3,oneof"`
+}
+
+func (*WatchRequest_CreateRequest) isWatchRequest_RequestUnion() {}
+
+func (*WatchRequest_CancelRequest) isWatchRequest_RequestUnion() {}
+
+func (*WatchRequest_ProgressRequest) isWatchRequest_RequestUnion() {}
+
type WatchCreateRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// key is the key to register for watching.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// range_end is the end of the range [key, range_end) to watch. If range_end is not given,
@@ -2008,145 +2125,138 @@ type WatchCreateRequest struct {
// use on the stream will cause an error to be returned.
WatchId int64 `protobuf:"varint,7,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
// fragment enables splitting large revisions into multiple watch responses.
- Fragment bool `protobuf:"varint,8,opt,name=fragment,proto3" json:"fragment,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Fragment bool `protobuf:"varint,8,opt,name=fragment,proto3" json:"fragment,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} }
-func (m *WatchCreateRequest) String() string { return proto.CompactTextString(m) }
-func (*WatchCreateRequest) ProtoMessage() {}
-func (*WatchCreateRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{21}
-}
-func (m *WatchCreateRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *WatchCreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_WatchCreateRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *WatchCreateRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_WatchCreateRequest.Merge(m, src)
+func (x *WatchCreateRequest) Reset() {
+ *x = WatchCreateRequest{}
+ mi := &file_rpc_proto_msgTypes[21]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WatchCreateRequest) XXX_Size() int {
- return m.Size()
+
+func (x *WatchCreateRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *WatchCreateRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_WatchCreateRequest.DiscardUnknown(m)
+
+func (*WatchCreateRequest) ProtoMessage() {}
+
+func (x *WatchCreateRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[21]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_WatchCreateRequest proto.InternalMessageInfo
+// Deprecated: Use WatchCreateRequest.ProtoReflect.Descriptor instead.
+func (*WatchCreateRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{21}
+}
-func (m *WatchCreateRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *WatchCreateRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
return nil
}
-func (m *WatchCreateRequest) GetRangeEnd() []byte {
- if m != nil {
- return m.RangeEnd
+func (x *WatchCreateRequest) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
return nil
}
-func (m *WatchCreateRequest) GetStartRevision() int64 {
- if m != nil {
- return m.StartRevision
+func (x *WatchCreateRequest) GetStartRevision() int64 {
+ if x != nil {
+ return x.StartRevision
}
return 0
}
-func (m *WatchCreateRequest) GetProgressNotify() bool {
- if m != nil {
- return m.ProgressNotify
+func (x *WatchCreateRequest) GetProgressNotify() bool {
+ if x != nil {
+ return x.ProgressNotify
}
return false
}
-func (m *WatchCreateRequest) GetFilters() []WatchCreateRequest_FilterType {
- if m != nil {
- return m.Filters
+func (x *WatchCreateRequest) GetFilters() []WatchCreateRequest_FilterType {
+ if x != nil {
+ return x.Filters
}
return nil
}
-func (m *WatchCreateRequest) GetPrevKv() bool {
- if m != nil {
- return m.PrevKv
+func (x *WatchCreateRequest) GetPrevKv() bool {
+ if x != nil {
+ return x.PrevKv
}
return false
}
-func (m *WatchCreateRequest) GetWatchId() int64 {
- if m != nil {
- return m.WatchId
+func (x *WatchCreateRequest) GetWatchId() int64 {
+ if x != nil {
+ return x.WatchId
}
return 0
}
-func (m *WatchCreateRequest) GetFragment() bool {
- if m != nil {
- return m.Fragment
+func (x *WatchCreateRequest) GetFragment() bool {
+ if x != nil {
+ return x.Fragment
}
return false
}
type WatchCancelRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// watch_id is the watcher id to cancel so that no more events are transmitted.
- WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WatchCancelRequest) Reset() { *m = WatchCancelRequest{} }
-func (m *WatchCancelRequest) String() string { return proto.CompactTextString(m) }
-func (*WatchCancelRequest) ProtoMessage() {}
-func (*WatchCancelRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{22}
-}
-func (m *WatchCancelRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *WatchCancelRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_WatchCancelRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *WatchCancelRequest) Reset() {
+ *x = WatchCancelRequest{}
+ mi := &file_rpc_proto_msgTypes[22]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WatchCancelRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_WatchCancelRequest.Merge(m, src)
-}
-func (m *WatchCancelRequest) XXX_Size() int {
- return m.Size()
+
+func (x *WatchCancelRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *WatchCancelRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_WatchCancelRequest.DiscardUnknown(m)
+
+func (*WatchCancelRequest) ProtoMessage() {}
+
+func (x *WatchCancelRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[22]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_WatchCancelRequest proto.InternalMessageInfo
+// Deprecated: Use WatchCancelRequest.ProtoReflect.Descriptor instead.
+func (*WatchCancelRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{22}
+}
-func (m *WatchCancelRequest) GetWatchId() int64 {
- if m != nil {
- return m.WatchId
+func (x *WatchCancelRequest) GetWatchId() int64 {
+ if x != nil {
+ return x.WatchId
}
return 0
}
@@ -2154,46 +2264,44 @@ func (m *WatchCancelRequest) GetWatchId() int64 {
// Requests the a watch stream progress status be sent in the watch response stream as soon as
// possible.
type WatchProgressRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WatchProgressRequest) Reset() { *m = WatchProgressRequest{} }
-func (m *WatchProgressRequest) String() string { return proto.CompactTextString(m) }
-func (*WatchProgressRequest) ProtoMessage() {}
-func (*WatchProgressRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{23}
-}
-func (m *WatchProgressRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *WatchProgressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_WatchProgressRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *WatchProgressRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_WatchProgressRequest.Merge(m, src)
+func (x *WatchProgressRequest) Reset() {
+ *x = WatchProgressRequest{}
+ mi := &file_rpc_proto_msgTypes[23]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WatchProgressRequest) XXX_Size() int {
- return m.Size()
+
+func (x *WatchProgressRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *WatchProgressRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_WatchProgressRequest.DiscardUnknown(m)
+
+func (*WatchProgressRequest) ProtoMessage() {}
+
+func (x *WatchProgressRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[23]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_WatchProgressRequest proto.InternalMessageInfo
+// Deprecated: Use WatchProgressRequest.ProtoReflect.Descriptor instead.
+func (*WatchProgressRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{23}
+}
type WatchResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// watch_id is the ID of the watcher that corresponds to the response.
WatchId int64 `protobuf:"varint,2,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
// created is set to true if the response is for a create watch request.
@@ -2217,650 +2325,617 @@ type WatchResponse struct {
// cancel_reason indicates the reason for canceling the watcher.
CancelReason string `protobuf:"bytes,6,opt,name=cancel_reason,json=cancelReason,proto3" json:"cancel_reason,omitempty"`
// framgment is true if large watch response was split over multiple responses.
- Fragment bool `protobuf:"varint,7,opt,name=fragment,proto3" json:"fragment,omitempty"`
- Events []*mvccpb.Event `protobuf:"bytes,11,rep,name=events,proto3" json:"events,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Fragment bool `protobuf:"varint,7,opt,name=fragment,proto3" json:"fragment,omitempty"`
+ Events []*mvccpb.Event `protobuf:"bytes,11,rep,name=events,proto3" json:"events,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *WatchResponse) Reset() { *m = WatchResponse{} }
-func (m *WatchResponse) String() string { return proto.CompactTextString(m) }
-func (*WatchResponse) ProtoMessage() {}
-func (*WatchResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{24}
-}
-func (m *WatchResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *WatchResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_WatchResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *WatchResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_WatchResponse.Merge(m, src)
+func (x *WatchResponse) Reset() {
+ *x = WatchResponse{}
+ mi := &file_rpc_proto_msgTypes[24]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *WatchResponse) XXX_Size() int {
- return m.Size()
+
+func (x *WatchResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *WatchResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_WatchResponse.DiscardUnknown(m)
+
+func (*WatchResponse) ProtoMessage() {}
+
+func (x *WatchResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[24]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_WatchResponse proto.InternalMessageInfo
+// Deprecated: Use WatchResponse.ProtoReflect.Descriptor instead.
+func (*WatchResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{24}
+}
-func (m *WatchResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *WatchResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *WatchResponse) GetWatchId() int64 {
- if m != nil {
- return m.WatchId
+func (x *WatchResponse) GetWatchId() int64 {
+ if x != nil {
+ return x.WatchId
}
return 0
}
-func (m *WatchResponse) GetCreated() bool {
- if m != nil {
- return m.Created
+func (x *WatchResponse) GetCreated() bool {
+ if x != nil {
+ return x.Created
}
return false
}
-func (m *WatchResponse) GetCanceled() bool {
- if m != nil {
- return m.Canceled
+func (x *WatchResponse) GetCanceled() bool {
+ if x != nil {
+ return x.Canceled
}
return false
}
-func (m *WatchResponse) GetCompactRevision() int64 {
- if m != nil {
- return m.CompactRevision
+func (x *WatchResponse) GetCompactRevision() int64 {
+ if x != nil {
+ return x.CompactRevision
}
return 0
}
-func (m *WatchResponse) GetCancelReason() string {
- if m != nil {
- return m.CancelReason
+func (x *WatchResponse) GetCancelReason() string {
+ if x != nil {
+ return x.CancelReason
}
return ""
}
-func (m *WatchResponse) GetFragment() bool {
- if m != nil {
- return m.Fragment
+func (x *WatchResponse) GetFragment() bool {
+ if x != nil {
+ return x.Fragment
}
return false
}
-func (m *WatchResponse) GetEvents() []*mvccpb.Event {
- if m != nil {
- return m.Events
+func (x *WatchResponse) GetEvents() []*mvccpb.Event {
+ if x != nil {
+ return x.Events
}
return nil
}
type LeaseGrantRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// TTL is the advisory time-to-live in seconds. Expired lease will return -1.
TTL int64 `protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"`
// ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID.
- ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseGrantRequest) Reset() { *m = LeaseGrantRequest{} }
-func (m *LeaseGrantRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseGrantRequest) ProtoMessage() {}
-func (*LeaseGrantRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{25}
-}
-func (m *LeaseGrantRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseGrantRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseGrantRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseGrantRequest) Reset() {
+ *x = LeaseGrantRequest{}
+ mi := &file_rpc_proto_msgTypes[25]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseGrantRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseGrantRequest.Merge(m, src)
-}
-func (m *LeaseGrantRequest) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseGrantRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseGrantRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseGrantRequest.DiscardUnknown(m)
+
+func (*LeaseGrantRequest) ProtoMessage() {}
+
+func (x *LeaseGrantRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[25]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseGrantRequest proto.InternalMessageInfo
+// Deprecated: Use LeaseGrantRequest.ProtoReflect.Descriptor instead.
+func (*LeaseGrantRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{25}
+}
-func (m *LeaseGrantRequest) GetTTL() int64 {
- if m != nil {
- return m.TTL
+func (x *LeaseGrantRequest) GetTTL() int64 {
+ if x != nil {
+ return x.TTL
}
return 0
}
-func (m *LeaseGrantRequest) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseGrantRequest) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type LeaseGrantResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID for the granted lease.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the server chosen lease time-to-live in seconds.
- TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
- Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
+ Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseGrantResponse) Reset() { *m = LeaseGrantResponse{} }
-func (m *LeaseGrantResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseGrantResponse) ProtoMessage() {}
-func (*LeaseGrantResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{26}
-}
-func (m *LeaseGrantResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseGrantResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseGrantResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseGrantResponse) Reset() {
+ *x = LeaseGrantResponse{}
+ mi := &file_rpc_proto_msgTypes[26]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseGrantResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseGrantResponse.Merge(m, src)
-}
-func (m *LeaseGrantResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseGrantResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseGrantResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseGrantResponse.DiscardUnknown(m)
+
+func (*LeaseGrantResponse) ProtoMessage() {}
+
+func (x *LeaseGrantResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[26]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseGrantResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseGrantResponse.ProtoReflect.Descriptor instead.
+func (*LeaseGrantResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{26}
+}
-func (m *LeaseGrantResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseGrantResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *LeaseGrantResponse) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseGrantResponse) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *LeaseGrantResponse) GetTTL() int64 {
- if m != nil {
- return m.TTL
+func (x *LeaseGrantResponse) GetTTL() int64 {
+ if x != nil {
+ return x.TTL
}
return 0
}
-func (m *LeaseGrantResponse) GetError() string {
- if m != nil {
- return m.Error
+func (x *LeaseGrantResponse) GetError() string {
+ if x != nil {
+ return x.Error
}
return ""
}
type LeaseRevokeRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted.
- ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseRevokeRequest) Reset() { *m = LeaseRevokeRequest{} }
-func (m *LeaseRevokeRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseRevokeRequest) ProtoMessage() {}
-func (*LeaseRevokeRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{27}
-}
-func (m *LeaseRevokeRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseRevokeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseRevokeRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseRevokeRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseRevokeRequest.Merge(m, src)
+func (x *LeaseRevokeRequest) Reset() {
+ *x = LeaseRevokeRequest{}
+ mi := &file_rpc_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseRevokeRequest) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseRevokeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseRevokeRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseRevokeRequest.DiscardUnknown(m)
+
+func (*LeaseRevokeRequest) ProtoMessage() {}
+
+func (x *LeaseRevokeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[27]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseRevokeRequest proto.InternalMessageInfo
+// Deprecated: Use LeaseRevokeRequest.ProtoReflect.Descriptor instead.
+func (*LeaseRevokeRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{27}
+}
-func (m *LeaseRevokeRequest) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseRevokeRequest) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type LeaseRevokeResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseRevokeResponse) Reset() { *m = LeaseRevokeResponse{} }
-func (m *LeaseRevokeResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseRevokeResponse) ProtoMessage() {}
-func (*LeaseRevokeResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{28}
-}
-func (m *LeaseRevokeResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseRevokeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseRevokeResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseRevokeResponse) Reset() {
+ *x = LeaseRevokeResponse{}
+ mi := &file_rpc_proto_msgTypes[28]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseRevokeResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseRevokeResponse.Merge(m, src)
-}
-func (m *LeaseRevokeResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseRevokeResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseRevokeResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseRevokeResponse.DiscardUnknown(m)
+
+func (*LeaseRevokeResponse) ProtoMessage() {}
+
+func (x *LeaseRevokeResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[28]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseRevokeResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseRevokeResponse.ProtoReflect.Descriptor instead.
+func (*LeaseRevokeResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{28}
+}
-func (m *LeaseRevokeResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseRevokeResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type LeaseCheckpoint struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the lease ID to checkpoint.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// Remaining_TTL is the remaining time until expiry of the lease.
- Remaining_TTL int64 `protobuf:"varint,2,opt,name=remaining_TTL,json=remainingTTL,proto3" json:"remaining_TTL,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Remaining_TTL int64 `protobuf:"varint,2,opt,name=remaining_TTL,json=remainingTTL,proto3" json:"remaining_TTL,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseCheckpoint) Reset() { *m = LeaseCheckpoint{} }
-func (m *LeaseCheckpoint) String() string { return proto.CompactTextString(m) }
-func (*LeaseCheckpoint) ProtoMessage() {}
-func (*LeaseCheckpoint) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{29}
-}
-func (m *LeaseCheckpoint) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseCheckpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseCheckpoint.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseCheckpoint) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseCheckpoint.Merge(m, src)
+func (x *LeaseCheckpoint) Reset() {
+ *x = LeaseCheckpoint{}
+ mi := &file_rpc_proto_msgTypes[29]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseCheckpoint) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseCheckpoint) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseCheckpoint) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseCheckpoint.DiscardUnknown(m)
+
+func (*LeaseCheckpoint) ProtoMessage() {}
+
+func (x *LeaseCheckpoint) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[29]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseCheckpoint proto.InternalMessageInfo
+// Deprecated: Use LeaseCheckpoint.ProtoReflect.Descriptor instead.
+func (*LeaseCheckpoint) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{29}
+}
-func (m *LeaseCheckpoint) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseCheckpoint) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *LeaseCheckpoint) GetRemaining_TTL() int64 {
- if m != nil {
- return m.Remaining_TTL
+func (x *LeaseCheckpoint) GetRemaining_TTL() int64 {
+ if x != nil {
+ return x.Remaining_TTL
}
return 0
}
type LeaseCheckpointRequest struct {
- Checkpoints []*LeaseCheckpoint `protobuf:"bytes,1,rep,name=checkpoints,proto3" json:"checkpoints,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Checkpoints []*LeaseCheckpoint `protobuf:"bytes,1,rep,name=checkpoints,proto3" json:"checkpoints,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseCheckpointRequest) Reset() { *m = LeaseCheckpointRequest{} }
-func (m *LeaseCheckpointRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseCheckpointRequest) ProtoMessage() {}
-func (*LeaseCheckpointRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{30}
-}
-func (m *LeaseCheckpointRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseCheckpointRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseCheckpointRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseCheckpointRequest) Reset() {
+ *x = LeaseCheckpointRequest{}
+ mi := &file_rpc_proto_msgTypes[30]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseCheckpointRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseCheckpointRequest.Merge(m, src)
-}
-func (m *LeaseCheckpointRequest) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseCheckpointRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseCheckpointRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseCheckpointRequest.DiscardUnknown(m)
+
+func (*LeaseCheckpointRequest) ProtoMessage() {}
+
+func (x *LeaseCheckpointRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[30]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseCheckpointRequest proto.InternalMessageInfo
+// Deprecated: Use LeaseCheckpointRequest.ProtoReflect.Descriptor instead.
+func (*LeaseCheckpointRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{30}
+}
-func (m *LeaseCheckpointRequest) GetCheckpoints() []*LeaseCheckpoint {
- if m != nil {
- return m.Checkpoints
+func (x *LeaseCheckpointRequest) GetCheckpoints() []*LeaseCheckpoint {
+ if x != nil {
+ return x.Checkpoints
}
return nil
}
type LeaseCheckpointResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseCheckpointResponse) Reset() { *m = LeaseCheckpointResponse{} }
-func (m *LeaseCheckpointResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseCheckpointResponse) ProtoMessage() {}
-func (*LeaseCheckpointResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{31}
-}
-func (m *LeaseCheckpointResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseCheckpointResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseCheckpointResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseCheckpointResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseCheckpointResponse.Merge(m, src)
+func (x *LeaseCheckpointResponse) Reset() {
+ *x = LeaseCheckpointResponse{}
+ mi := &file_rpc_proto_msgTypes[31]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseCheckpointResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseCheckpointResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseCheckpointResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseCheckpointResponse.DiscardUnknown(m)
+
+func (*LeaseCheckpointResponse) ProtoMessage() {}
+
+func (x *LeaseCheckpointResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[31]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseCheckpointResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseCheckpointResponse.ProtoReflect.Descriptor instead.
+func (*LeaseCheckpointResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{31}
+}
-func (m *LeaseCheckpointResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseCheckpointResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type LeaseKeepAliveRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the lease ID for the lease to keep alive.
- ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseKeepAliveRequest) Reset() { *m = LeaseKeepAliveRequest{} }
-func (m *LeaseKeepAliveRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseKeepAliveRequest) ProtoMessage() {}
-func (*LeaseKeepAliveRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{32}
-}
-func (m *LeaseKeepAliveRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseKeepAliveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseKeepAliveRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseKeepAliveRequest) Reset() {
+ *x = LeaseKeepAliveRequest{}
+ mi := &file_rpc_proto_msgTypes[32]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseKeepAliveRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseKeepAliveRequest.Merge(m, src)
-}
-func (m *LeaseKeepAliveRequest) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseKeepAliveRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseKeepAliveRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseKeepAliveRequest.DiscardUnknown(m)
+
+func (*LeaseKeepAliveRequest) ProtoMessage() {}
+
+func (x *LeaseKeepAliveRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[32]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseKeepAliveRequest proto.InternalMessageInfo
+// Deprecated: Use LeaseKeepAliveRequest.ProtoReflect.Descriptor instead.
+func (*LeaseKeepAliveRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{32}
+}
-func (m *LeaseKeepAliveRequest) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseKeepAliveRequest) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type LeaseKeepAliveResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID from the keep alive request.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the new time-to-live for the lease.
- TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ TTL int64 `protobuf:"varint,3,opt,name=TTL,proto3" json:"TTL,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseKeepAliveResponse) Reset() { *m = LeaseKeepAliveResponse{} }
-func (m *LeaseKeepAliveResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseKeepAliveResponse) ProtoMessage() {}
-func (*LeaseKeepAliveResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{33}
-}
-func (m *LeaseKeepAliveResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseKeepAliveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseKeepAliveResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseKeepAliveResponse) Reset() {
+ *x = LeaseKeepAliveResponse{}
+ mi := &file_rpc_proto_msgTypes[33]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseKeepAliveResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseKeepAliveResponse.Merge(m, src)
-}
-func (m *LeaseKeepAliveResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseKeepAliveResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseKeepAliveResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseKeepAliveResponse.DiscardUnknown(m)
+
+func (*LeaseKeepAliveResponse) ProtoMessage() {}
+
+func (x *LeaseKeepAliveResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[33]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseKeepAliveResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseKeepAliveResponse.ProtoReflect.Descriptor instead.
+func (*LeaseKeepAliveResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{33}
+}
-func (m *LeaseKeepAliveResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseKeepAliveResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *LeaseKeepAliveResponse) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseKeepAliveResponse) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *LeaseKeepAliveResponse) GetTTL() int64 {
- if m != nil {
- return m.TTL
+func (x *LeaseKeepAliveResponse) GetTTL() int64 {
+ if x != nil {
+ return x.TTL
}
return 0
}
type LeaseTimeToLiveRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the lease ID for the lease.
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// keys is true to query all the keys attached to this lease.
- Keys bool `protobuf:"varint,2,opt,name=keys,proto3" json:"keys,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Keys bool `protobuf:"varint,2,opt,name=keys,proto3" json:"keys,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseTimeToLiveRequest) Reset() { *m = LeaseTimeToLiveRequest{} }
-func (m *LeaseTimeToLiveRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseTimeToLiveRequest) ProtoMessage() {}
-func (*LeaseTimeToLiveRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{34}
-}
-func (m *LeaseTimeToLiveRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseTimeToLiveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseTimeToLiveRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseTimeToLiveRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseTimeToLiveRequest.Merge(m, src)
+func (x *LeaseTimeToLiveRequest) Reset() {
+ *x = LeaseTimeToLiveRequest{}
+ mi := &file_rpc_proto_msgTypes[34]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseTimeToLiveRequest) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseTimeToLiveRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseTimeToLiveRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseTimeToLiveRequest.DiscardUnknown(m)
+
+func (*LeaseTimeToLiveRequest) ProtoMessage() {}
+
+func (x *LeaseTimeToLiveRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[34]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseTimeToLiveRequest proto.InternalMessageInfo
+// Deprecated: Use LeaseTimeToLiveRequest.ProtoReflect.Descriptor instead.
+func (*LeaseTimeToLiveRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{34}
+}
-func (m *LeaseTimeToLiveRequest) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseTimeToLiveRequest) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *LeaseTimeToLiveRequest) GetKeys() bool {
- if m != nil {
- return m.Keys
+func (x *LeaseTimeToLiveRequest) GetKeys() bool {
+ if x != nil {
+ return x.Keys
}
return false
}
type LeaseTimeToLiveResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// ID is the lease ID from the keep alive request.
ID int64 `protobuf:"varint,2,opt,name=ID,proto3" json:"ID,omitempty"`
// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds.
@@ -2868,222 +2943,210 @@ type LeaseTimeToLiveResponse struct {
// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
GrantedTTL int64 `protobuf:"varint,4,opt,name=grantedTTL,proto3" json:"grantedTTL,omitempty"`
// Keys is the list of keys attached to this lease.
- Keys [][]byte `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Keys [][]byte `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseTimeToLiveResponse) Reset() { *m = LeaseTimeToLiveResponse{} }
-func (m *LeaseTimeToLiveResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseTimeToLiveResponse) ProtoMessage() {}
-func (*LeaseTimeToLiveResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{35}
-}
-func (m *LeaseTimeToLiveResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseTimeToLiveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseTimeToLiveResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseTimeToLiveResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseTimeToLiveResponse.Merge(m, src)
+func (x *LeaseTimeToLiveResponse) Reset() {
+ *x = LeaseTimeToLiveResponse{}
+ mi := &file_rpc_proto_msgTypes[35]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseTimeToLiveResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseTimeToLiveResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseTimeToLiveResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseTimeToLiveResponse.DiscardUnknown(m)
+
+func (*LeaseTimeToLiveResponse) ProtoMessage() {}
+
+func (x *LeaseTimeToLiveResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[35]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseTimeToLiveResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseTimeToLiveResponse.ProtoReflect.Descriptor instead.
+func (*LeaseTimeToLiveResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{35}
+}
-func (m *LeaseTimeToLiveResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseTimeToLiveResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *LeaseTimeToLiveResponse) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseTimeToLiveResponse) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *LeaseTimeToLiveResponse) GetTTL() int64 {
- if m != nil {
- return m.TTL
+func (x *LeaseTimeToLiveResponse) GetTTL() int64 {
+ if x != nil {
+ return x.TTL
}
return 0
}
-func (m *LeaseTimeToLiveResponse) GetGrantedTTL() int64 {
- if m != nil {
- return m.GrantedTTL
+func (x *LeaseTimeToLiveResponse) GetGrantedTTL() int64 {
+ if x != nil {
+ return x.GrantedTTL
}
return 0
}
-func (m *LeaseTimeToLiveResponse) GetKeys() [][]byte {
- if m != nil {
- return m.Keys
+func (x *LeaseTimeToLiveResponse) GetKeys() [][]byte {
+ if x != nil {
+ return x.Keys
}
return nil
}
type LeaseLeasesRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseLeasesRequest) Reset() { *m = LeaseLeasesRequest{} }
-func (m *LeaseLeasesRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseLeasesRequest) ProtoMessage() {}
-func (*LeaseLeasesRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{36}
-}
-func (m *LeaseLeasesRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseLeasesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseLeasesRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *LeaseLeasesRequest) Reset() {
+ *x = LeaseLeasesRequest{}
+ mi := &file_rpc_proto_msgTypes[36]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *LeaseLeasesRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LeaseLeasesRequest) ProtoMessage() {}
+
+func (x *LeaseLeasesRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[36]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *LeaseLeasesRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseLeasesRequest.Merge(m, src)
-}
-func (m *LeaseLeasesRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use LeaseLeasesRequest.ProtoReflect.Descriptor instead.
+func (*LeaseLeasesRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{36}
}
-func (m *LeaseLeasesRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseLeasesRequest.DiscardUnknown(m)
+
+type LeaseStatus struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` // TODO: int64 TTL = 2;
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_LeaseLeasesRequest proto.InternalMessageInfo
+func (x *LeaseStatus) Reset() {
+ *x = LeaseStatus{}
+ mi := &file_rpc_proto_msgTypes[37]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type LeaseStatus struct {
- ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *LeaseStatus) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseStatus) Reset() { *m = LeaseStatus{} }
-func (m *LeaseStatus) String() string { return proto.CompactTextString(m) }
-func (*LeaseStatus) ProtoMessage() {}
-func (*LeaseStatus) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{37}
-}
-func (m *LeaseStatus) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseStatus.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*LeaseStatus) ProtoMessage() {}
+
+func (x *LeaseStatus) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[37]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *LeaseStatus) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseStatus.Merge(m, src)
-}
-func (m *LeaseStatus) XXX_Size() int {
- return m.Size()
-}
-func (m *LeaseStatus) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseStatus.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseStatus proto.InternalMessageInfo
+// Deprecated: Use LeaseStatus.ProtoReflect.Descriptor instead.
+func (*LeaseStatus) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{37}
+}
-func (m *LeaseStatus) GetID() int64 {
- if m != nil {
- return m.ID
+func (x *LeaseStatus) GetID() int64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type LeaseLeasesResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Leases []*LeaseStatus `protobuf:"bytes,2,rep,name=leases,proto3" json:"leases,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Leases []*LeaseStatus `protobuf:"bytes,2,rep,name=leases,proto3" json:"leases,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseLeasesResponse) Reset() { *m = LeaseLeasesResponse{} }
-func (m *LeaseLeasesResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseLeasesResponse) ProtoMessage() {}
-func (*LeaseLeasesResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{38}
-}
-func (m *LeaseLeasesResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseLeasesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseLeasesResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *LeaseLeasesResponse) Reset() {
+ *x = LeaseLeasesResponse{}
+ mi := &file_rpc_proto_msgTypes[38]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseLeasesResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseLeasesResponse.Merge(m, src)
-}
-func (m *LeaseLeasesResponse) XXX_Size() int {
- return m.Size()
+
+func (x *LeaseLeasesResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseLeasesResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseLeasesResponse.DiscardUnknown(m)
+
+func (*LeaseLeasesResponse) ProtoMessage() {}
+
+func (x *LeaseLeasesResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[38]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LeaseLeasesResponse proto.InternalMessageInfo
+// Deprecated: Use LeaseLeasesResponse.ProtoReflect.Descriptor instead.
+func (*LeaseLeasesResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{38}
+}
-func (m *LeaseLeasesResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LeaseLeasesResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *LeaseLeasesResponse) GetLeases() []*LeaseStatus {
- if m != nil {
- return m.Leases
+func (x *LeaseLeasesResponse) GetLeases() []*LeaseStatus {
+ if x != nil {
+ return x.Leases
}
return nil
}
type Member struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the member ID for this member.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// name is the human-readable name of the member. If the member is not started, the name will be an empty string.
@@ -3093,808 +3156,763 @@ type Member struct {
// clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty.
ClientURLs []string `protobuf:"bytes,4,rep,name=clientURLs,proto3" json:"clientURLs,omitempty"`
// isLearner indicates if the member is raft learner.
- IsLearner bool `protobuf:"varint,5,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ IsLearner bool `protobuf:"varint,5,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Member) Reset() { *m = Member{} }
-func (m *Member) String() string { return proto.CompactTextString(m) }
-func (*Member) ProtoMessage() {}
-func (*Member) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{39}
-}
-func (m *Member) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Member) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Member.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Member) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Member.Merge(m, src)
+func (x *Member) Reset() {
+ *x = Member{}
+ mi := &file_rpc_proto_msgTypes[39]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Member) XXX_Size() int {
- return m.Size()
+
+func (x *Member) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Member) XXX_DiscardUnknown() {
- xxx_messageInfo_Member.DiscardUnknown(m)
+
+func (*Member) ProtoMessage() {}
+
+func (x *Member) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[39]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_Member proto.InternalMessageInfo
+// Deprecated: Use Member.ProtoReflect.Descriptor instead.
+func (*Member) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{39}
+}
-func (m *Member) GetID() uint64 {
- if m != nil {
- return m.ID
+func (x *Member) GetID() uint64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *Member) GetName() string {
- if m != nil {
- return m.Name
+func (x *Member) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *Member) GetPeerURLs() []string {
- if m != nil {
- return m.PeerURLs
+func (x *Member) GetPeerURLs() []string {
+ if x != nil {
+ return x.PeerURLs
}
return nil
}
-func (m *Member) GetClientURLs() []string {
- if m != nil {
- return m.ClientURLs
+func (x *Member) GetClientURLs() []string {
+ if x != nil {
+ return x.ClientURLs
}
return nil
}
-func (m *Member) GetIsLearner() bool {
- if m != nil {
- return m.IsLearner
+func (x *Member) GetIsLearner() bool {
+ if x != nil {
+ return x.IsLearner
}
return false
}
type MemberAddRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// peerURLs is the list of URLs the added member will use to communicate with the cluster.
PeerURLs []string `protobuf:"bytes,1,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
// isLearner indicates if the added member is raft learner.
- IsLearner bool `protobuf:"varint,2,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ IsLearner bool `protobuf:"varint,2,opt,name=isLearner,proto3" json:"isLearner,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberAddRequest) Reset() { *m = MemberAddRequest{} }
-func (m *MemberAddRequest) String() string { return proto.CompactTextString(m) }
-func (*MemberAddRequest) ProtoMessage() {}
-func (*MemberAddRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{40}
-}
-func (m *MemberAddRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberAddRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberAddRequest) Reset() {
+ *x = MemberAddRequest{}
+ mi := &file_rpc_proto_msgTypes[40]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberAddRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberAddRequest.Merge(m, src)
-}
-func (m *MemberAddRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MemberAddRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberAddRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberAddRequest.DiscardUnknown(m)
+
+func (*MemberAddRequest) ProtoMessage() {}
+
+func (x *MemberAddRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[40]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberAddRequest proto.InternalMessageInfo
+// Deprecated: Use MemberAddRequest.ProtoReflect.Descriptor instead.
+func (*MemberAddRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{40}
+}
-func (m *MemberAddRequest) GetPeerURLs() []string {
- if m != nil {
- return m.PeerURLs
+func (x *MemberAddRequest) GetPeerURLs() []string {
+ if x != nil {
+ return x.PeerURLs
}
return nil
}
-func (m *MemberAddRequest) GetIsLearner() bool {
- if m != nil {
- return m.IsLearner
+func (x *MemberAddRequest) GetIsLearner() bool {
+ if x != nil {
+ return x.IsLearner
}
return false
}
type MemberAddResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// member is the member information for the added member.
Member *Member `protobuf:"bytes,2,opt,name=member,proto3" json:"member,omitempty"`
// members is a list of all members after adding the new member.
- Members []*Member `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Members []*Member `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberAddResponse) Reset() { *m = MemberAddResponse{} }
-func (m *MemberAddResponse) String() string { return proto.CompactTextString(m) }
-func (*MemberAddResponse) ProtoMessage() {}
-func (*MemberAddResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{41}
-}
-func (m *MemberAddResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberAddResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberAddResponse) Reset() {
+ *x = MemberAddResponse{}
+ mi := &file_rpc_proto_msgTypes[41]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberAddResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberAddResponse.Merge(m, src)
-}
-func (m *MemberAddResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MemberAddResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberAddResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberAddResponse.DiscardUnknown(m)
+
+func (*MemberAddResponse) ProtoMessage() {}
+
+func (x *MemberAddResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[41]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberAddResponse proto.InternalMessageInfo
+// Deprecated: Use MemberAddResponse.ProtoReflect.Descriptor instead.
+func (*MemberAddResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{41}
+}
-func (m *MemberAddResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MemberAddResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *MemberAddResponse) GetMember() *Member {
- if m != nil {
- return m.Member
+func (x *MemberAddResponse) GetMember() *Member {
+ if x != nil {
+ return x.Member
}
return nil
}
-func (m *MemberAddResponse) GetMembers() []*Member {
- if m != nil {
- return m.Members
+func (x *MemberAddResponse) GetMembers() []*Member {
+ if x != nil {
+ return x.Members
}
return nil
}
type MemberRemoveRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the member ID of the member to remove.
- ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberRemoveRequest) Reset() { *m = MemberRemoveRequest{} }
-func (m *MemberRemoveRequest) String() string { return proto.CompactTextString(m) }
-func (*MemberRemoveRequest) ProtoMessage() {}
-func (*MemberRemoveRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{42}
-}
-func (m *MemberRemoveRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberRemoveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberRemoveRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *MemberRemoveRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberRemoveRequest.Merge(m, src)
+func (x *MemberRemoveRequest) Reset() {
+ *x = MemberRemoveRequest{}
+ mi := &file_rpc_proto_msgTypes[42]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberRemoveRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MemberRemoveRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberRemoveRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberRemoveRequest.DiscardUnknown(m)
+
+func (*MemberRemoveRequest) ProtoMessage() {}
+
+func (x *MemberRemoveRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[42]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberRemoveRequest proto.InternalMessageInfo
+// Deprecated: Use MemberRemoveRequest.ProtoReflect.Descriptor instead.
+func (*MemberRemoveRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{42}
+}
-func (m *MemberRemoveRequest) GetID() uint64 {
- if m != nil {
- return m.ID
+func (x *MemberRemoveRequest) GetID() uint64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type MemberRemoveResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after removing the member.
- Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberRemoveResponse) Reset() { *m = MemberRemoveResponse{} }
-func (m *MemberRemoveResponse) String() string { return proto.CompactTextString(m) }
-func (*MemberRemoveResponse) ProtoMessage() {}
-func (*MemberRemoveResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{43}
-}
-func (m *MemberRemoveResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberRemoveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberRemoveResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *MemberRemoveResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberRemoveResponse.Merge(m, src)
+func (x *MemberRemoveResponse) Reset() {
+ *x = MemberRemoveResponse{}
+ mi := &file_rpc_proto_msgTypes[43]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberRemoveResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MemberRemoveResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberRemoveResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberRemoveResponse.DiscardUnknown(m)
+
+func (*MemberRemoveResponse) ProtoMessage() {}
+
+func (x *MemberRemoveResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[43]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberRemoveResponse proto.InternalMessageInfo
+// Deprecated: Use MemberRemoveResponse.ProtoReflect.Descriptor instead.
+func (*MemberRemoveResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{43}
+}
-func (m *MemberRemoveResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MemberRemoveResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *MemberRemoveResponse) GetMembers() []*Member {
- if m != nil {
- return m.Members
+func (x *MemberRemoveResponse) GetMembers() []*Member {
+ if x != nil {
+ return x.Members
}
return nil
}
type MemberUpdateRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the member ID of the member to update.
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
// peerURLs is the new list of URLs the member will use to communicate with the cluster.
- PeerURLs []string `protobuf:"bytes,2,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ PeerURLs []string `protobuf:"bytes,2,rep,name=peerURLs,proto3" json:"peerURLs,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberUpdateRequest) Reset() { *m = MemberUpdateRequest{} }
-func (m *MemberUpdateRequest) String() string { return proto.CompactTextString(m) }
-func (*MemberUpdateRequest) ProtoMessage() {}
-func (*MemberUpdateRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{44}
-}
-func (m *MemberUpdateRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberUpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberUpdateRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberUpdateRequest) Reset() {
+ *x = MemberUpdateRequest{}
+ mi := &file_rpc_proto_msgTypes[44]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberUpdateRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberUpdateRequest.Merge(m, src)
-}
-func (m *MemberUpdateRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MemberUpdateRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberUpdateRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberUpdateRequest.DiscardUnknown(m)
+
+func (*MemberUpdateRequest) ProtoMessage() {}
+
+func (x *MemberUpdateRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[44]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberUpdateRequest proto.InternalMessageInfo
+// Deprecated: Use MemberUpdateRequest.ProtoReflect.Descriptor instead.
+func (*MemberUpdateRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{44}
+}
-func (m *MemberUpdateRequest) GetID() uint64 {
- if m != nil {
- return m.ID
+func (x *MemberUpdateRequest) GetID() uint64 {
+ if x != nil {
+ return x.ID
}
return 0
}
-func (m *MemberUpdateRequest) GetPeerURLs() []string {
- if m != nil {
- return m.PeerURLs
+func (x *MemberUpdateRequest) GetPeerURLs() []string {
+ if x != nil {
+ return x.PeerURLs
}
return nil
}
type MemberUpdateResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after updating the member.
- Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberUpdateResponse) Reset() { *m = MemberUpdateResponse{} }
-func (m *MemberUpdateResponse) String() string { return proto.CompactTextString(m) }
-func (*MemberUpdateResponse) ProtoMessage() {}
-func (*MemberUpdateResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{45}
-}
-func (m *MemberUpdateResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberUpdateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberUpdateResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberUpdateResponse) Reset() {
+ *x = MemberUpdateResponse{}
+ mi := &file_rpc_proto_msgTypes[45]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberUpdateResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberUpdateResponse.Merge(m, src)
-}
-func (m *MemberUpdateResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MemberUpdateResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberUpdateResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberUpdateResponse.DiscardUnknown(m)
+
+func (*MemberUpdateResponse) ProtoMessage() {}
+
+func (x *MemberUpdateResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[45]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberUpdateResponse proto.InternalMessageInfo
+// Deprecated: Use MemberUpdateResponse.ProtoReflect.Descriptor instead.
+func (*MemberUpdateResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{45}
+}
-func (m *MemberUpdateResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MemberUpdateResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *MemberUpdateResponse) GetMembers() []*Member {
- if m != nil {
- return m.Members
+func (x *MemberUpdateResponse) GetMembers() []*Member {
+ if x != nil {
+ return x.Members
}
return nil
}
type MemberListRequest struct {
- Linearizable bool `protobuf:"varint,1,opt,name=linearizable,proto3" json:"linearizable,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Linearizable bool `protobuf:"varint,1,opt,name=linearizable,proto3" json:"linearizable,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberListRequest) Reset() { *m = MemberListRequest{} }
-func (m *MemberListRequest) String() string { return proto.CompactTextString(m) }
-func (*MemberListRequest) ProtoMessage() {}
-func (*MemberListRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{46}
-}
-func (m *MemberListRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberListRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *MemberListRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberListRequest.Merge(m, src)
+func (x *MemberListRequest) Reset() {
+ *x = MemberListRequest{}
+ mi := &file_rpc_proto_msgTypes[46]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberListRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MemberListRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberListRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberListRequest.DiscardUnknown(m)
+
+func (*MemberListRequest) ProtoMessage() {}
+
+func (x *MemberListRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[46]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberListRequest proto.InternalMessageInfo
+// Deprecated: Use MemberListRequest.ProtoReflect.Descriptor instead.
+func (*MemberListRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{46}
+}
-func (m *MemberListRequest) GetLinearizable() bool {
- if m != nil {
- return m.Linearizable
+func (x *MemberListRequest) GetLinearizable() bool {
+ if x != nil {
+ return x.Linearizable
}
return false
}
type MemberListResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members associated with the cluster.
- Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberListResponse) Reset() { *m = MemberListResponse{} }
-func (m *MemberListResponse) String() string { return proto.CompactTextString(m) }
-func (*MemberListResponse) ProtoMessage() {}
-func (*MemberListResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{47}
-}
-func (m *MemberListResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberListResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *MemberListResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberListResponse.Merge(m, src)
+func (x *MemberListResponse) Reset() {
+ *x = MemberListResponse{}
+ mi := &file_rpc_proto_msgTypes[47]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberListResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MemberListResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberListResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberListResponse.DiscardUnknown(m)
+
+func (*MemberListResponse) ProtoMessage() {}
+
+func (x *MemberListResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[47]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberListResponse proto.InternalMessageInfo
+// Deprecated: Use MemberListResponse.ProtoReflect.Descriptor instead.
+func (*MemberListResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{47}
+}
-func (m *MemberListResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MemberListResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *MemberListResponse) GetMembers() []*Member {
- if m != nil {
- return m.Members
+func (x *MemberListResponse) GetMembers() []*Member {
+ if x != nil {
+ return x.Members
}
return nil
}
type MemberPromoteRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID is the member ID of the member to promote.
- ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberPromoteRequest) Reset() { *m = MemberPromoteRequest{} }
-func (m *MemberPromoteRequest) String() string { return proto.CompactTextString(m) }
-func (*MemberPromoteRequest) ProtoMessage() {}
-func (*MemberPromoteRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{48}
-}
-func (m *MemberPromoteRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberPromoteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberPromoteRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberPromoteRequest) Reset() {
+ *x = MemberPromoteRequest{}
+ mi := &file_rpc_proto_msgTypes[48]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberPromoteRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberPromoteRequest.Merge(m, src)
-}
-func (m *MemberPromoteRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MemberPromoteRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberPromoteRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberPromoteRequest.DiscardUnknown(m)
+
+func (*MemberPromoteRequest) ProtoMessage() {}
+
+func (x *MemberPromoteRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[48]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberPromoteRequest proto.InternalMessageInfo
+// Deprecated: Use MemberPromoteRequest.ProtoReflect.Descriptor instead.
+func (*MemberPromoteRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{48}
+}
-func (m *MemberPromoteRequest) GetID() uint64 {
- if m != nil {
- return m.ID
+func (x *MemberPromoteRequest) GetID() uint64 {
+ if x != nil {
+ return x.ID
}
return 0
}
type MemberPromoteResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// members is a list of all members after promoting the member.
- Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Members []*Member `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MemberPromoteResponse) Reset() { *m = MemberPromoteResponse{} }
-func (m *MemberPromoteResponse) String() string { return proto.CompactTextString(m) }
-func (*MemberPromoteResponse) ProtoMessage() {}
-func (*MemberPromoteResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{49}
-}
-func (m *MemberPromoteResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MemberPromoteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MemberPromoteResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MemberPromoteResponse) Reset() {
+ *x = MemberPromoteResponse{}
+ mi := &file_rpc_proto_msgTypes[49]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MemberPromoteResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MemberPromoteResponse.Merge(m, src)
-}
-func (m *MemberPromoteResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MemberPromoteResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MemberPromoteResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MemberPromoteResponse.DiscardUnknown(m)
+
+func (*MemberPromoteResponse) ProtoMessage() {}
+
+func (x *MemberPromoteResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[49]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MemberPromoteResponse proto.InternalMessageInfo
+// Deprecated: Use MemberPromoteResponse.ProtoReflect.Descriptor instead.
+func (*MemberPromoteResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{49}
+}
-func (m *MemberPromoteResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MemberPromoteResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *MemberPromoteResponse) GetMembers() []*Member {
- if m != nil {
- return m.Members
+func (x *MemberPromoteResponse) GetMembers() []*Member {
+ if x != nil {
+ return x.Members
}
return nil
}
type DefragmentRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DefragmentRequest) Reset() { *m = DefragmentRequest{} }
-func (m *DefragmentRequest) String() string { return proto.CompactTextString(m) }
-func (*DefragmentRequest) ProtoMessage() {}
-func (*DefragmentRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{50}
-}
-func (m *DefragmentRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DefragmentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DefragmentRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *DefragmentRequest) Reset() {
+ *x = DefragmentRequest{}
+ mi := &file_rpc_proto_msgTypes[50]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *DefragmentRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DefragmentRequest) ProtoMessage() {}
+
+func (x *DefragmentRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[50]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *DefragmentRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DefragmentRequest.Merge(m, src)
-}
-func (m *DefragmentRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use DefragmentRequest.ProtoReflect.Descriptor instead.
+func (*DefragmentRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{50}
}
-func (m *DefragmentRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_DefragmentRequest.DiscardUnknown(m)
+
+type DefragmentResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_DefragmentRequest proto.InternalMessageInfo
+func (x *DefragmentResponse) Reset() {
+ *x = DefragmentResponse{}
+ mi := &file_rpc_proto_msgTypes[51]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type DefragmentResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *DefragmentResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DefragmentResponse) Reset() { *m = DefragmentResponse{} }
-func (m *DefragmentResponse) String() string { return proto.CompactTextString(m) }
-func (*DefragmentResponse) ProtoMessage() {}
-func (*DefragmentResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{51}
-}
-func (m *DefragmentResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DefragmentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DefragmentResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*DefragmentResponse) ProtoMessage() {}
+
+func (x *DefragmentResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[51]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *DefragmentResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DefragmentResponse.Merge(m, src)
-}
-func (m *DefragmentResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *DefragmentResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_DefragmentResponse.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DefragmentResponse proto.InternalMessageInfo
+// Deprecated: Use DefragmentResponse.ProtoReflect.Descriptor instead.
+func (*DefragmentResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{51}
+}
-func (m *DefragmentResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *DefragmentResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type MoveLeaderRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// targetID is the node ID for the new leader.
- TargetID uint64 `protobuf:"varint,1,opt,name=targetID,proto3" json:"targetID,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ TargetID uint64 `protobuf:"varint,1,opt,name=targetID,proto3" json:"targetID,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MoveLeaderRequest) Reset() { *m = MoveLeaderRequest{} }
-func (m *MoveLeaderRequest) String() string { return proto.CompactTextString(m) }
-func (*MoveLeaderRequest) ProtoMessage() {}
-func (*MoveLeaderRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{52}
-}
-func (m *MoveLeaderRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MoveLeaderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MoveLeaderRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *MoveLeaderRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MoveLeaderRequest.Merge(m, src)
+func (x *MoveLeaderRequest) Reset() {
+ *x = MoveLeaderRequest{}
+ mi := &file_rpc_proto_msgTypes[52]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MoveLeaderRequest) XXX_Size() int {
- return m.Size()
+
+func (x *MoveLeaderRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MoveLeaderRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_MoveLeaderRequest.DiscardUnknown(m)
+
+func (*MoveLeaderRequest) ProtoMessage() {}
+
+func (x *MoveLeaderRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[52]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MoveLeaderRequest proto.InternalMessageInfo
+// Deprecated: Use MoveLeaderRequest.ProtoReflect.Descriptor instead.
+func (*MoveLeaderRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{52}
+}
-func (m *MoveLeaderRequest) GetTargetID() uint64 {
- if m != nil {
- return m.TargetID
+func (x *MoveLeaderRequest) GetTargetID() uint64 {
+ if x != nil {
+ return x.TargetID
}
return 0
}
type MoveLeaderResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *MoveLeaderResponse) Reset() { *m = MoveLeaderResponse{} }
-func (m *MoveLeaderResponse) String() string { return proto.CompactTextString(m) }
-func (*MoveLeaderResponse) ProtoMessage() {}
-func (*MoveLeaderResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{53}
-}
-func (m *MoveLeaderResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *MoveLeaderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_MoveLeaderResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *MoveLeaderResponse) Reset() {
+ *x = MoveLeaderResponse{}
+ mi := &file_rpc_proto_msgTypes[53]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *MoveLeaderResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_MoveLeaderResponse.Merge(m, src)
-}
-func (m *MoveLeaderResponse) XXX_Size() int {
- return m.Size()
+
+func (x *MoveLeaderResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *MoveLeaderResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_MoveLeaderResponse.DiscardUnknown(m)
+
+func (*MoveLeaderResponse) ProtoMessage() {}
+
+func (x *MoveLeaderResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[53]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_MoveLeaderResponse proto.InternalMessageInfo
+// Deprecated: Use MoveLeaderResponse.ProtoReflect.Descriptor instead.
+func (*MoveLeaderResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{53}
+}
-func (m *MoveLeaderResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *MoveLeaderResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type AlarmRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// action is the kind of alarm request to issue. The action
// may GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a
// raised alarm.
@@ -3903,290 +3921,274 @@ type AlarmRequest struct {
// alarm request covers all members.
MemberID uint64 `protobuf:"varint,2,opt,name=memberID,proto3" json:"memberID,omitempty"`
// alarm is the type of alarm to consider for this request.
- Alarm AlarmType `protobuf:"varint,3,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Alarm AlarmType `protobuf:"varint,3,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AlarmRequest) Reset() { *m = AlarmRequest{} }
-func (m *AlarmRequest) String() string { return proto.CompactTextString(m) }
-func (*AlarmRequest) ProtoMessage() {}
-func (*AlarmRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{54}
-}
-func (m *AlarmRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AlarmRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AlarmRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AlarmRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AlarmRequest.Merge(m, src)
+func (x *AlarmRequest) Reset() {
+ *x = AlarmRequest{}
+ mi := &file_rpc_proto_msgTypes[54]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AlarmRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AlarmRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AlarmRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AlarmRequest.DiscardUnknown(m)
+
+func (*AlarmRequest) ProtoMessage() {}
+
+func (x *AlarmRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[54]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AlarmRequest proto.InternalMessageInfo
+// Deprecated: Use AlarmRequest.ProtoReflect.Descriptor instead.
+func (*AlarmRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{54}
+}
-func (m *AlarmRequest) GetAction() AlarmRequest_AlarmAction {
- if m != nil {
- return m.Action
+func (x *AlarmRequest) GetAction() AlarmRequest_AlarmAction {
+ if x != nil {
+ return x.Action
}
return AlarmRequest_GET
}
-func (m *AlarmRequest) GetMemberID() uint64 {
- if m != nil {
- return m.MemberID
+func (x *AlarmRequest) GetMemberID() uint64 {
+ if x != nil {
+ return x.MemberID
}
return 0
}
-func (m *AlarmRequest) GetAlarm() AlarmType {
- if m != nil {
- return m.Alarm
+func (x *AlarmRequest) GetAlarm() AlarmType {
+ if x != nil {
+ return x.Alarm
}
return AlarmType_NONE
}
type AlarmMember struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// memberID is the ID of the member associated with the raised alarm.
MemberID uint64 `protobuf:"varint,1,opt,name=memberID,proto3" json:"memberID,omitempty"`
// alarm is the type of alarm which has been raised.
- Alarm AlarmType `protobuf:"varint,2,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Alarm AlarmType `protobuf:"varint,2,opt,name=alarm,proto3,enum=etcdserverpb.AlarmType" json:"alarm,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AlarmMember) Reset() { *m = AlarmMember{} }
-func (m *AlarmMember) String() string { return proto.CompactTextString(m) }
-func (*AlarmMember) ProtoMessage() {}
-func (*AlarmMember) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{55}
-}
-func (m *AlarmMember) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AlarmMember) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AlarmMember.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AlarmMember) Reset() {
+ *x = AlarmMember{}
+ mi := &file_rpc_proto_msgTypes[55]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AlarmMember) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AlarmMember.Merge(m, src)
-}
-func (m *AlarmMember) XXX_Size() int {
- return m.Size()
+
+func (x *AlarmMember) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AlarmMember) XXX_DiscardUnknown() {
- xxx_messageInfo_AlarmMember.DiscardUnknown(m)
+
+func (*AlarmMember) ProtoMessage() {}
+
+func (x *AlarmMember) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[55]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AlarmMember proto.InternalMessageInfo
+// Deprecated: Use AlarmMember.ProtoReflect.Descriptor instead.
+func (*AlarmMember) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{55}
+}
-func (m *AlarmMember) GetMemberID() uint64 {
- if m != nil {
- return m.MemberID
+func (x *AlarmMember) GetMemberID() uint64 {
+ if x != nil {
+ return x.MemberID
}
return 0
}
-func (m *AlarmMember) GetAlarm() AlarmType {
- if m != nil {
- return m.Alarm
+func (x *AlarmMember) GetAlarm() AlarmType {
+ if x != nil {
+ return x.Alarm
}
return AlarmType_NONE
}
type AlarmResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// alarms is a list of alarms associated with the alarm request.
- Alarms []*AlarmMember `protobuf:"bytes,2,rep,name=alarms,proto3" json:"alarms,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Alarms []*AlarmMember `protobuf:"bytes,2,rep,name=alarms,proto3" json:"alarms,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AlarmResponse) Reset() { *m = AlarmResponse{} }
-func (m *AlarmResponse) String() string { return proto.CompactTextString(m) }
-func (*AlarmResponse) ProtoMessage() {}
-func (*AlarmResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{56}
-}
-func (m *AlarmResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AlarmResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AlarmResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AlarmResponse) Reset() {
+ *x = AlarmResponse{}
+ mi := &file_rpc_proto_msgTypes[56]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AlarmResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AlarmResponse.Merge(m, src)
-}
-func (m *AlarmResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AlarmResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AlarmResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AlarmResponse.DiscardUnknown(m)
+
+func (*AlarmResponse) ProtoMessage() {}
+
+func (x *AlarmResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[56]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AlarmResponse proto.InternalMessageInfo
+// Deprecated: Use AlarmResponse.ProtoReflect.Descriptor instead.
+func (*AlarmResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{56}
+}
-func (m *AlarmResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AlarmResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *AlarmResponse) GetAlarms() []*AlarmMember {
- if m != nil {
- return m.Alarms
+func (x *AlarmResponse) GetAlarms() []*AlarmMember {
+ if x != nil {
+ return x.Alarms
}
return nil
}
type DowngradeRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// action is the kind of downgrade request to issue. The action may
// VALIDATE the target version, DOWNGRADE the cluster version,
// or CANCEL the current downgrading job.
Action DowngradeRequest_DowngradeAction `protobuf:"varint,1,opt,name=action,proto3,enum=etcdserverpb.DowngradeRequest_DowngradeAction" json:"action,omitempty"`
// version is the target version to downgrade.
- Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DowngradeRequest) Reset() { *m = DowngradeRequest{} }
-func (m *DowngradeRequest) String() string { return proto.CompactTextString(m) }
-func (*DowngradeRequest) ProtoMessage() {}
-func (*DowngradeRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{57}
-}
-func (m *DowngradeRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DowngradeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DowngradeRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *DowngradeRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DowngradeRequest.Merge(m, src)
+func (x *DowngradeRequest) Reset() {
+ *x = DowngradeRequest{}
+ mi := &file_rpc_proto_msgTypes[57]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeRequest) XXX_Size() int {
- return m.Size()
+
+func (x *DowngradeRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_DowngradeRequest.DiscardUnknown(m)
+
+func (*DowngradeRequest) ProtoMessage() {}
+
+func (x *DowngradeRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[57]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DowngradeRequest proto.InternalMessageInfo
+// Deprecated: Use DowngradeRequest.ProtoReflect.Descriptor instead.
+func (*DowngradeRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{57}
+}
-func (m *DowngradeRequest) GetAction() DowngradeRequest_DowngradeAction {
- if m != nil {
- return m.Action
+func (x *DowngradeRequest) GetAction() DowngradeRequest_DowngradeAction {
+ if x != nil {
+ return x.Action
}
return DowngradeRequest_VALIDATE
}
-func (m *DowngradeRequest) GetVersion() string {
- if m != nil {
- return m.Version
+func (x *DowngradeRequest) GetVersion() string {
+ if x != nil {
+ return x.Version
}
return ""
}
type DowngradeResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// version is the current cluster version.
- Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DowngradeResponse) Reset() { *m = DowngradeResponse{} }
-func (m *DowngradeResponse) String() string { return proto.CompactTextString(m) }
-func (*DowngradeResponse) ProtoMessage() {}
-func (*DowngradeResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{58}
-}
-func (m *DowngradeResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DowngradeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DowngradeResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *DowngradeResponse) Reset() {
+ *x = DowngradeResponse{}
+ mi := &file_rpc_proto_msgTypes[58]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DowngradeResponse.Merge(m, src)
-}
-func (m *DowngradeResponse) XXX_Size() int {
- return m.Size()
+
+func (x *DowngradeResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_DowngradeResponse.DiscardUnknown(m)
+
+func (*DowngradeResponse) ProtoMessage() {}
+
+func (x *DowngradeResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[58]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DowngradeResponse proto.InternalMessageInfo
+// Deprecated: Use DowngradeResponse.ProtoReflect.Descriptor instead.
+func (*DowngradeResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{58}
+}
-func (m *DowngradeResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *DowngradeResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *DowngradeResponse) GetVersion() string {
- if m != nil {
- return m.Version
+func (x *DowngradeResponse) GetVersion() string {
+ if x != nil {
+ return x.Version
}
return ""
}
@@ -4196,93 +4198,88 @@ func (m *DowngradeResponse) GetVersion() string {
// target version is less than this version, then the downgrade(online)
// or migration(offline) isn't safe, so shouldn't be allowed.
type DowngradeVersionTestRequest struct {
- Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DowngradeVersionTestRequest) Reset() { *m = DowngradeVersionTestRequest{} }
-func (m *DowngradeVersionTestRequest) String() string { return proto.CompactTextString(m) }
-func (*DowngradeVersionTestRequest) ProtoMessage() {}
-func (*DowngradeVersionTestRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{59}
-}
-func (m *DowngradeVersionTestRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DowngradeVersionTestRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DowngradeVersionTestRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *DowngradeVersionTestRequest) Reset() {
+ *x = DowngradeVersionTestRequest{}
+ mi := &file_rpc_proto_msgTypes[59]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeVersionTestRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DowngradeVersionTestRequest.Merge(m, src)
-}
-func (m *DowngradeVersionTestRequest) XXX_Size() int {
- return m.Size()
+
+func (x *DowngradeVersionTestRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeVersionTestRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_DowngradeVersionTestRequest.DiscardUnknown(m)
+
+func (*DowngradeVersionTestRequest) ProtoMessage() {}
+
+func (x *DowngradeVersionTestRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[59]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DowngradeVersionTestRequest proto.InternalMessageInfo
+// Deprecated: Use DowngradeVersionTestRequest.ProtoReflect.Descriptor instead.
+func (*DowngradeVersionTestRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{59}
+}
-func (m *DowngradeVersionTestRequest) GetVer() string {
- if m != nil {
- return m.Ver
+func (x *DowngradeVersionTestRequest) GetVer() string {
+ if x != nil {
+ return x.Ver
}
return ""
}
type StatusRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *StatusRequest) Reset() { *m = StatusRequest{} }
-func (m *StatusRequest) String() string { return proto.CompactTextString(m) }
-func (*StatusRequest) ProtoMessage() {}
-func (*StatusRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{60}
-}
-func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *StatusRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_StatusRequest.Merge(m, src)
+func (x *StatusRequest) Reset() {
+ *x = StatusRequest{}
+ mi := &file_rpc_proto_msgTypes[60]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *StatusRequest) XXX_Size() int {
- return m.Size()
+
+func (x *StatusRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *StatusRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_StatusRequest.DiscardUnknown(m)
+
+func (*StatusRequest) ProtoMessage() {}
+
+func (x *StatusRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[60]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_StatusRequest proto.InternalMessageInfo
+// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead.
+func (*StatusRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{60}
+}
type StatusResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// version is the cluster protocol version used by the responding member.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
// dbSize is the size of the backend database physically allocated, in bytes, of the responding member.
@@ -4306,22240 +4303,2650 @@ type StatusResponse struct {
// dbSizeQuota is the configured etcd storage quota in bytes (the value passed to etcd instance by flag --quota-backend-bytes)
DbSizeQuota int64 `protobuf:"varint,12,opt,name=dbSizeQuota,proto3" json:"dbSizeQuota,omitempty"`
// downgradeInfo indicates if there is downgrade process.
- DowngradeInfo *DowngradeInfo `protobuf:"bytes,13,opt,name=downgradeInfo,proto3" json:"downgradeInfo,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ DowngradeInfo *DowngradeInfo `protobuf:"bytes,13,opt,name=downgradeInfo,proto3" json:"downgradeInfo,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *StatusResponse) Reset() { *m = StatusResponse{} }
-func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
-func (*StatusResponse) ProtoMessage() {}
-func (*StatusResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{61}
-}
-func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *StatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_StatusResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *StatusResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_StatusResponse.Merge(m, src)
+func (x *StatusResponse) Reset() {
+ *x = StatusResponse{}
+ mi := &file_rpc_proto_msgTypes[61]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *StatusResponse) XXX_Size() int {
- return m.Size()
+
+func (x *StatusResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *StatusResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_StatusResponse.DiscardUnknown(m)
+
+func (*StatusResponse) ProtoMessage() {}
+
+func (x *StatusResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[61]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_StatusResponse proto.InternalMessageInfo
+// Deprecated: Use StatusResponse.ProtoReflect.Descriptor instead.
+func (*StatusResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{61}
+}
-func (m *StatusResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *StatusResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *StatusResponse) GetVersion() string {
- if m != nil {
- return m.Version
+func (x *StatusResponse) GetVersion() string {
+ if x != nil {
+ return x.Version
}
return ""
}
-func (m *StatusResponse) GetDbSize() int64 {
- if m != nil {
- return m.DbSize
+func (x *StatusResponse) GetDbSize() int64 {
+ if x != nil {
+ return x.DbSize
}
return 0
}
-func (m *StatusResponse) GetLeader() uint64 {
- if m != nil {
- return m.Leader
+func (x *StatusResponse) GetLeader() uint64 {
+ if x != nil {
+ return x.Leader
}
return 0
}
-func (m *StatusResponse) GetRaftIndex() uint64 {
- if m != nil {
- return m.RaftIndex
+func (x *StatusResponse) GetRaftIndex() uint64 {
+ if x != nil {
+ return x.RaftIndex
}
return 0
}
-func (m *StatusResponse) GetRaftTerm() uint64 {
- if m != nil {
- return m.RaftTerm
+func (x *StatusResponse) GetRaftTerm() uint64 {
+ if x != nil {
+ return x.RaftTerm
}
return 0
}
-func (m *StatusResponse) GetRaftAppliedIndex() uint64 {
- if m != nil {
- return m.RaftAppliedIndex
+func (x *StatusResponse) GetRaftAppliedIndex() uint64 {
+ if x != nil {
+ return x.RaftAppliedIndex
}
return 0
}
-func (m *StatusResponse) GetErrors() []string {
- if m != nil {
- return m.Errors
+func (x *StatusResponse) GetErrors() []string {
+ if x != nil {
+ return x.Errors
}
return nil
}
-func (m *StatusResponse) GetDbSizeInUse() int64 {
- if m != nil {
- return m.DbSizeInUse
+func (x *StatusResponse) GetDbSizeInUse() int64 {
+ if x != nil {
+ return x.DbSizeInUse
}
return 0
}
-func (m *StatusResponse) GetIsLearner() bool {
- if m != nil {
- return m.IsLearner
+func (x *StatusResponse) GetIsLearner() bool {
+ if x != nil {
+ return x.IsLearner
}
return false
}
-func (m *StatusResponse) GetStorageVersion() string {
- if m != nil {
- return m.StorageVersion
+func (x *StatusResponse) GetStorageVersion() string {
+ if x != nil {
+ return x.StorageVersion
}
return ""
}
-func (m *StatusResponse) GetDbSizeQuota() int64 {
- if m != nil {
- return m.DbSizeQuota
+func (x *StatusResponse) GetDbSizeQuota() int64 {
+ if x != nil {
+ return x.DbSizeQuota
}
return 0
}
-func (m *StatusResponse) GetDowngradeInfo() *DowngradeInfo {
- if m != nil {
- return m.DowngradeInfo
+func (x *StatusResponse) GetDowngradeInfo() *DowngradeInfo {
+ if x != nil {
+ return x.DowngradeInfo
}
return nil
}
type DowngradeInfo struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// enabled indicates whether the cluster is enabled to downgrade.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// targetVersion is the target downgrade version.
- TargetVersion string `protobuf:"bytes,2,opt,name=targetVersion,proto3" json:"targetVersion,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ TargetVersion string `protobuf:"bytes,2,opt,name=targetVersion,proto3" json:"targetVersion,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DowngradeInfo) Reset() { *m = DowngradeInfo{} }
-func (m *DowngradeInfo) String() string { return proto.CompactTextString(m) }
-func (*DowngradeInfo) ProtoMessage() {}
-func (*DowngradeInfo) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{62}
-}
-func (m *DowngradeInfo) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DowngradeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DowngradeInfo.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *DowngradeInfo) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DowngradeInfo.Merge(m, src)
+func (x *DowngradeInfo) Reset() {
+ *x = DowngradeInfo{}
+ mi := &file_rpc_proto_msgTypes[62]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeInfo) XXX_Size() int {
- return m.Size()
+
+func (x *DowngradeInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeInfo) XXX_DiscardUnknown() {
- xxx_messageInfo_DowngradeInfo.DiscardUnknown(m)
+
+func (*DowngradeInfo) ProtoMessage() {}
+
+func (x *DowngradeInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[62]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_DowngradeInfo proto.InternalMessageInfo
+// Deprecated: Use DowngradeInfo.ProtoReflect.Descriptor instead.
+func (*DowngradeInfo) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{62}
+}
-func (m *DowngradeInfo) GetEnabled() bool {
- if m != nil {
- return m.Enabled
+func (x *DowngradeInfo) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
}
return false
}
-func (m *DowngradeInfo) GetTargetVersion() string {
- if m != nil {
- return m.TargetVersion
+func (x *DowngradeInfo) GetTargetVersion() string {
+ if x != nil {
+ return x.TargetVersion
}
return ""
}
type AuthEnableRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthEnableRequest) Reset() { *m = AuthEnableRequest{} }
-func (m *AuthEnableRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthEnableRequest) ProtoMessage() {}
-func (*AuthEnableRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{63}
-}
-func (m *AuthEnableRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthEnableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthEnableRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *AuthEnableRequest) Reset() {
+ *x = AuthEnableRequest{}
+ mi := &file_rpc_proto_msgTypes[63]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *AuthEnableRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AuthEnableRequest) ProtoMessage() {}
+
+func (x *AuthEnableRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[63]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *AuthEnableRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthEnableRequest.Merge(m, src)
-}
-func (m *AuthEnableRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use AuthEnableRequest.ProtoReflect.Descriptor instead.
+func (*AuthEnableRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{63}
}
-func (m *AuthEnableRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthEnableRequest.DiscardUnknown(m)
+
+type AuthDisableRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthEnableRequest proto.InternalMessageInfo
+func (x *AuthDisableRequest) Reset() {
+ *x = AuthDisableRequest{}
+ mi := &file_rpc_proto_msgTypes[64]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type AuthDisableRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *AuthDisableRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthDisableRequest) Reset() { *m = AuthDisableRequest{} }
-func (m *AuthDisableRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthDisableRequest) ProtoMessage() {}
-func (*AuthDisableRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{64}
-}
-func (m *AuthDisableRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthDisableRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthDisableRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*AuthDisableRequest) ProtoMessage() {}
+
+func (x *AuthDisableRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[64]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *AuthDisableRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthDisableRequest.Merge(m, src)
-}
-func (m *AuthDisableRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use AuthDisableRequest.ProtoReflect.Descriptor instead.
+func (*AuthDisableRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{64}
}
-func (m *AuthDisableRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthDisableRequest.DiscardUnknown(m)
+
+type AuthStatusRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthDisableRequest proto.InternalMessageInfo
+func (x *AuthStatusRequest) Reset() {
+ *x = AuthStatusRequest{}
+ mi := &file_rpc_proto_msgTypes[65]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type AuthStatusRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *AuthStatusRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthStatusRequest) Reset() { *m = AuthStatusRequest{} }
-func (m *AuthStatusRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthStatusRequest) ProtoMessage() {}
-func (*AuthStatusRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{65}
-}
-func (m *AuthStatusRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthStatusRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*AuthStatusRequest) ProtoMessage() {}
+
+func (x *AuthStatusRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[65]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *AuthStatusRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthStatusRequest.Merge(m, src)
-}
-func (m *AuthStatusRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use AuthStatusRequest.ProtoReflect.Descriptor instead.
+func (*AuthStatusRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{65}
}
-func (m *AuthStatusRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthStatusRequest.DiscardUnknown(m)
+
+type AuthenticateRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthStatusRequest proto.InternalMessageInfo
+func (x *AuthenticateRequest) Reset() {
+ *x = AuthenticateRequest{}
+ mi := &file_rpc_proto_msgTypes[66]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type AuthenticateRequest struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *AuthenticateRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthenticateRequest) Reset() { *m = AuthenticateRequest{} }
-func (m *AuthenticateRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthenticateRequest) ProtoMessage() {}
-func (*AuthenticateRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{66}
-}
-func (m *AuthenticateRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthenticateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthenticateRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*AuthenticateRequest) ProtoMessage() {}
+
+func (x *AuthenticateRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[66]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *AuthenticateRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthenticateRequest.Merge(m, src)
-}
-func (m *AuthenticateRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthenticateRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthenticateRequest.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthenticateRequest proto.InternalMessageInfo
+// Deprecated: Use AuthenticateRequest.ProtoReflect.Descriptor instead.
+func (*AuthenticateRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{66}
+}
-func (m *AuthenticateRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthenticateRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *AuthenticateRequest) GetPassword() string {
- if m != nil {
- return m.Password
+func (x *AuthenticateRequest) GetPassword() string {
+ if x != nil {
+ return x.Password
}
return ""
}
type AuthUserAddRequest struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
- Options *authpb.UserAddOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"`
- HashedPassword string `protobuf:"bytes,4,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *AuthUserAddRequest) Reset() { *m = AuthUserAddRequest{} }
-func (m *AuthUserAddRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserAddRequest) ProtoMessage() {}
-func (*AuthUserAddRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{67}
-}
-func (m *AuthUserAddRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserAddRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ Options *authpb.UserAddOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"`
+ HashedPassword string `protobuf:"bytes,4,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserAddRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserAddRequest.Merge(m, src)
+
+func (x *AuthUserAddRequest) Reset() {
+ *x = AuthUserAddRequest{}
+ mi := &file_rpc_proto_msgTypes[67]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserAddRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserAddRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserAddRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserAddRequest.DiscardUnknown(m)
+
+func (*AuthUserAddRequest) ProtoMessage() {}
+
+func (x *AuthUserAddRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[67]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserAddRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserAddRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserAddRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{67}
+}
-func (m *AuthUserAddRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthUserAddRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *AuthUserAddRequest) GetPassword() string {
- if m != nil {
- return m.Password
+func (x *AuthUserAddRequest) GetPassword() string {
+ if x != nil {
+ return x.Password
}
return ""
}
-func (m *AuthUserAddRequest) GetOptions() *authpb.UserAddOptions {
- if m != nil {
- return m.Options
+func (x *AuthUserAddRequest) GetOptions() *authpb.UserAddOptions {
+ if x != nil {
+ return x.Options
}
return nil
}
-func (m *AuthUserAddRequest) GetHashedPassword() string {
- if m != nil {
- return m.HashedPassword
+func (x *AuthUserAddRequest) GetHashedPassword() string {
+ if x != nil {
+ return x.HashedPassword
}
return ""
}
type AuthUserGetRequest struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserGetRequest) Reset() { *m = AuthUserGetRequest{} }
-func (m *AuthUserGetRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserGetRequest) ProtoMessage() {}
-func (*AuthUserGetRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{68}
-}
-func (m *AuthUserGetRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserGetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserGetRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthUserGetRequest) Reset() {
+ *x = AuthUserGetRequest{}
+ mi := &file_rpc_proto_msgTypes[68]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserGetRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserGetRequest.Merge(m, src)
+
+func (x *AuthUserGetRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserGetRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthUserGetRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserGetRequest.DiscardUnknown(m)
+
+func (*AuthUserGetRequest) ProtoMessage() {}
+
+func (x *AuthUserGetRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[68]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserGetRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserGetRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserGetRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{68}
+}
-func (m *AuthUserGetRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthUserGetRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
type AuthUserDeleteRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// name is the name of the user to delete.
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserDeleteRequest) Reset() { *m = AuthUserDeleteRequest{} }
-func (m *AuthUserDeleteRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserDeleteRequest) ProtoMessage() {}
-func (*AuthUserDeleteRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{69}
-}
-func (m *AuthUserDeleteRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserDeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserDeleteRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthUserDeleteRequest) Reset() {
+ *x = AuthUserDeleteRequest{}
+ mi := &file_rpc_proto_msgTypes[69]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserDeleteRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserDeleteRequest.Merge(m, src)
-}
-func (m *AuthUserDeleteRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserDeleteRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserDeleteRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserDeleteRequest.DiscardUnknown(m)
+
+func (*AuthUserDeleteRequest) ProtoMessage() {}
+
+func (x *AuthUserDeleteRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[69]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserDeleteRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserDeleteRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserDeleteRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{69}
+}
-func (m *AuthUserDeleteRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthUserDeleteRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
type AuthUserChangePasswordRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// name is the name of the user whose password is being changed.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// password is the new password for the user. Note that this field will be removed in the API layer.
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
// hashedPassword is the new password for the user. Note that this field will be initialized in the API layer.
- HashedPassword string `protobuf:"bytes,3,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ HashedPassword string `protobuf:"bytes,3,opt,name=hashedPassword,proto3" json:"hashedPassword,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserChangePasswordRequest) Reset() { *m = AuthUserChangePasswordRequest{} }
-func (m *AuthUserChangePasswordRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserChangePasswordRequest) ProtoMessage() {}
-func (*AuthUserChangePasswordRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{70}
-}
-func (m *AuthUserChangePasswordRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserChangePasswordRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserChangePasswordRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthUserChangePasswordRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserChangePasswordRequest.Merge(m, src)
+func (x *AuthUserChangePasswordRequest) Reset() {
+ *x = AuthUserChangePasswordRequest{}
+ mi := &file_rpc_proto_msgTypes[70]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserChangePasswordRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserChangePasswordRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserChangePasswordRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserChangePasswordRequest.DiscardUnknown(m)
+
+func (*AuthUserChangePasswordRequest) ProtoMessage() {}
+
+func (x *AuthUserChangePasswordRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[70]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserChangePasswordRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserChangePasswordRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserChangePasswordRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{70}
+}
-func (m *AuthUserChangePasswordRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthUserChangePasswordRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *AuthUserChangePasswordRequest) GetPassword() string {
- if m != nil {
- return m.Password
+func (x *AuthUserChangePasswordRequest) GetPassword() string {
+ if x != nil {
+ return x.Password
}
return ""
}
-func (m *AuthUserChangePasswordRequest) GetHashedPassword() string {
- if m != nil {
- return m.HashedPassword
+func (x *AuthUserChangePasswordRequest) GetHashedPassword() string {
+ if x != nil {
+ return x.HashedPassword
}
return ""
}
type AuthUserGrantRoleRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// user is the name of the user which should be granted a given role.
User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
// role is the name of the role to grant to the user.
- Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserGrantRoleRequest) Reset() { *m = AuthUserGrantRoleRequest{} }
-func (m *AuthUserGrantRoleRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserGrantRoleRequest) ProtoMessage() {}
-func (*AuthUserGrantRoleRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{71}
-}
-func (m *AuthUserGrantRoleRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserGrantRoleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserGrantRoleRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthUserGrantRoleRequest) Reset() {
+ *x = AuthUserGrantRoleRequest{}
+ mi := &file_rpc_proto_msgTypes[71]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserGrantRoleRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserGrantRoleRequest.Merge(m, src)
-}
-func (m *AuthUserGrantRoleRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserGrantRoleRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserGrantRoleRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserGrantRoleRequest.DiscardUnknown(m)
+
+func (*AuthUserGrantRoleRequest) ProtoMessage() {}
+
+func (x *AuthUserGrantRoleRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[71]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserGrantRoleRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserGrantRoleRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserGrantRoleRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{71}
+}
-func (m *AuthUserGrantRoleRequest) GetUser() string {
- if m != nil {
- return m.User
+func (x *AuthUserGrantRoleRequest) GetUser() string {
+ if x != nil {
+ return x.User
}
return ""
}
-func (m *AuthUserGrantRoleRequest) GetRole() string {
- if m != nil {
- return m.Role
+func (x *AuthUserGrantRoleRequest) GetRole() string {
+ if x != nil {
+ return x.Role
}
return ""
}
type AuthUserRevokeRoleRequest struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Role string `protobuf:"bytes,2,opt,name=role,proto3" json:"role,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserRevokeRoleRequest) Reset() { *m = AuthUserRevokeRoleRequest{} }
-func (m *AuthUserRevokeRoleRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserRevokeRoleRequest) ProtoMessage() {}
-func (*AuthUserRevokeRoleRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{72}
-}
-func (m *AuthUserRevokeRoleRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserRevokeRoleRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserRevokeRoleRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthUserRevokeRoleRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserRevokeRoleRequest.Merge(m, src)
+func (x *AuthUserRevokeRoleRequest) Reset() {
+ *x = AuthUserRevokeRoleRequest{}
+ mi := &file_rpc_proto_msgTypes[72]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserRevokeRoleRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserRevokeRoleRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserRevokeRoleRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserRevokeRoleRequest.DiscardUnknown(m)
+
+func (*AuthUserRevokeRoleRequest) ProtoMessage() {}
+
+func (x *AuthUserRevokeRoleRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[72]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserRevokeRoleRequest proto.InternalMessageInfo
+// Deprecated: Use AuthUserRevokeRoleRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserRevokeRoleRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{72}
+}
-func (m *AuthUserRevokeRoleRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthUserRevokeRoleRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *AuthUserRevokeRoleRequest) GetRole() string {
- if m != nil {
- return m.Role
+func (x *AuthUserRevokeRoleRequest) GetRole() string {
+ if x != nil {
+ return x.Role
}
return ""
}
type AuthRoleAddRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// name is the name of the role to add to the authentication system.
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleAddRequest) Reset() { *m = AuthRoleAddRequest{} }
-func (m *AuthRoleAddRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleAddRequest) ProtoMessage() {}
-func (*AuthRoleAddRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{73}
-}
-func (m *AuthRoleAddRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleAddRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleAddRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthRoleAddRequest) Reset() {
+ *x = AuthRoleAddRequest{}
+ mi := &file_rpc_proto_msgTypes[73]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleAddRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleAddRequest.Merge(m, src)
-}
-func (m *AuthRoleAddRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleAddRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleAddRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleAddRequest.DiscardUnknown(m)
+
+func (*AuthRoleAddRequest) ProtoMessage() {}
+
+func (x *AuthRoleAddRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[73]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthRoleAddRequest proto.InternalMessageInfo
+// Deprecated: Use AuthRoleAddRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleAddRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{73}
+}
-func (m *AuthRoleAddRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthRoleAddRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
type AuthRoleGetRequest struct {
- Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleGetRequest) Reset() { *m = AuthRoleGetRequest{} }
-func (m *AuthRoleGetRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleGetRequest) ProtoMessage() {}
-func (*AuthRoleGetRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{74}
-}
-func (m *AuthRoleGetRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleGetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleGetRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthRoleGetRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleGetRequest.Merge(m, src)
+func (x *AuthRoleGetRequest) Reset() {
+ *x = AuthRoleGetRequest{}
+ mi := &file_rpc_proto_msgTypes[74]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleGetRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleGetRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleGetRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleGetRequest.DiscardUnknown(m)
+
+func (*AuthRoleGetRequest) ProtoMessage() {}
+
+func (x *AuthRoleGetRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[74]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthRoleGetRequest proto.InternalMessageInfo
+// Deprecated: Use AuthRoleGetRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleGetRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{74}
+}
-func (m *AuthRoleGetRequest) GetRole() string {
- if m != nil {
- return m.Role
+func (x *AuthRoleGetRequest) GetRole() string {
+ if x != nil {
+ return x.Role
}
return ""
}
type AuthUserListRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserListRequest) Reset() { *m = AuthUserListRequest{} }
-func (m *AuthUserListRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthUserListRequest) ProtoMessage() {}
-func (*AuthUserListRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{75}
-}
-func (m *AuthUserListRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserListRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *AuthUserListRequest) Reset() {
+ *x = AuthUserListRequest{}
+ mi := &file_rpc_proto_msgTypes[75]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *AuthUserListRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AuthUserListRequest) ProtoMessage() {}
+
+func (x *AuthUserListRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[75]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *AuthUserListRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserListRequest.Merge(m, src)
-}
-func (m *AuthUserListRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use AuthUserListRequest.ProtoReflect.Descriptor instead.
+func (*AuthUserListRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{75}
}
-func (m *AuthUserListRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserListRequest.DiscardUnknown(m)
+
+type AuthRoleListRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthUserListRequest proto.InternalMessageInfo
+func (x *AuthRoleListRequest) Reset() {
+ *x = AuthRoleListRequest{}
+ mi := &file_rpc_proto_msgTypes[76]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type AuthRoleListRequest struct {
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *AuthRoleListRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleListRequest) Reset() { *m = AuthRoleListRequest{} }
-func (m *AuthRoleListRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleListRequest) ProtoMessage() {}
-func (*AuthRoleListRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{76}
-}
-func (m *AuthRoleListRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleListRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*AuthRoleListRequest) ProtoMessage() {}
+
+func (x *AuthRoleListRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[76]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *AuthRoleListRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleListRequest.Merge(m, src)
-}
-func (m *AuthRoleListRequest) XXX_Size() int {
- return m.Size()
+
+// Deprecated: Use AuthRoleListRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleListRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{76}
}
-func (m *AuthRoleListRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleListRequest.DiscardUnknown(m)
+
+type AuthRoleDeleteRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthRoleListRequest proto.InternalMessageInfo
+func (x *AuthRoleDeleteRequest) Reset() {
+ *x = AuthRoleDeleteRequest{}
+ mi := &file_rpc_proto_msgTypes[77]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
-type AuthRoleDeleteRequest struct {
- Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *AuthRoleDeleteRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleDeleteRequest) Reset() { *m = AuthRoleDeleteRequest{} }
-func (m *AuthRoleDeleteRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleDeleteRequest) ProtoMessage() {}
-func (*AuthRoleDeleteRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{77}
-}
-func (m *AuthRoleDeleteRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleDeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleDeleteRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*AuthRoleDeleteRequest) ProtoMessage() {}
+
+func (x *AuthRoleDeleteRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[77]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *AuthRoleDeleteRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleDeleteRequest.Merge(m, src)
-}
-func (m *AuthRoleDeleteRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthRoleDeleteRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleDeleteRequest.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthRoleDeleteRequest proto.InternalMessageInfo
+// Deprecated: Use AuthRoleDeleteRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleDeleteRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{77}
+}
-func (m *AuthRoleDeleteRequest) GetRole() string {
- if m != nil {
- return m.Role
+func (x *AuthRoleDeleteRequest) GetRole() string {
+ if x != nil {
+ return x.Role
}
return ""
}
type AuthRoleGrantPermissionRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// name is the name of the role which will be granted the permission.
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// perm is the permission to grant to the role.
- Perm *authpb.Permission `protobuf:"bytes,2,opt,name=perm,proto3" json:"perm,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Perm *authpb.Permission `protobuf:"bytes,2,opt,name=perm,proto3" json:"perm,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleGrantPermissionRequest) Reset() { *m = AuthRoleGrantPermissionRequest{} }
-func (m *AuthRoleGrantPermissionRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleGrantPermissionRequest) ProtoMessage() {}
-func (*AuthRoleGrantPermissionRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{78}
-}
-func (m *AuthRoleGrantPermissionRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleGrantPermissionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleGrantPermissionRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthRoleGrantPermissionRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleGrantPermissionRequest.Merge(m, src)
+func (x *AuthRoleGrantPermissionRequest) Reset() {
+ *x = AuthRoleGrantPermissionRequest{}
+ mi := &file_rpc_proto_msgTypes[78]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleGrantPermissionRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleGrantPermissionRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleGrantPermissionRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleGrantPermissionRequest.DiscardUnknown(m)
+
+func (*AuthRoleGrantPermissionRequest) ProtoMessage() {}
+
+func (x *AuthRoleGrantPermissionRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[78]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthRoleGrantPermissionRequest proto.InternalMessageInfo
+// Deprecated: Use AuthRoleGrantPermissionRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleGrantPermissionRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{78}
+}
-func (m *AuthRoleGrantPermissionRequest) GetName() string {
- if m != nil {
- return m.Name
+func (x *AuthRoleGrantPermissionRequest) GetName() string {
+ if x != nil {
+ return x.Name
}
return ""
}
-func (m *AuthRoleGrantPermissionRequest) GetPerm() *authpb.Permission {
- if m != nil {
- return m.Perm
+func (x *AuthRoleGrantPermissionRequest) GetPerm() *authpb.Permission {
+ if x != nil {
+ return x.Perm
}
return nil
}
type AuthRoleRevokePermissionRequest struct {
- Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
- Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
- RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
+ Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+ RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleRevokePermissionRequest) Reset() { *m = AuthRoleRevokePermissionRequest{} }
-func (m *AuthRoleRevokePermissionRequest) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleRevokePermissionRequest) ProtoMessage() {}
-func (*AuthRoleRevokePermissionRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{79}
-}
-func (m *AuthRoleRevokePermissionRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleRevokePermissionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleRevokePermissionRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthRoleRevokePermissionRequest) Reset() {
+ *x = AuthRoleRevokePermissionRequest{}
+ mi := &file_rpc_proto_msgTypes[79]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleRevokePermissionRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleRevokePermissionRequest.Merge(m, src)
-}
-func (m *AuthRoleRevokePermissionRequest) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleRevokePermissionRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthRoleRevokePermissionRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleRevokePermissionRequest.DiscardUnknown(m)
+
+func (*AuthRoleRevokePermissionRequest) ProtoMessage() {}
+
+func (x *AuthRoleRevokePermissionRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[79]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthRoleRevokePermissionRequest proto.InternalMessageInfo
+// Deprecated: Use AuthRoleRevokePermissionRequest.ProtoReflect.Descriptor instead.
+func (*AuthRoleRevokePermissionRequest) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{79}
+}
-func (m *AuthRoleRevokePermissionRequest) GetRole() string {
- if m != nil {
- return m.Role
+func (x *AuthRoleRevokePermissionRequest) GetRole() string {
+ if x != nil {
+ return x.Role
}
return ""
}
-func (m *AuthRoleRevokePermissionRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+func (x *AuthRoleRevokePermissionRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
return nil
}
-func (m *AuthRoleRevokePermissionRequest) GetRangeEnd() []byte {
- if m != nil {
- return m.RangeEnd
+func (x *AuthRoleRevokePermissionRequest) GetRangeEnd() []byte {
+ if x != nil {
+ return x.RangeEnd
}
return nil
}
type AuthEnableResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthEnableResponse) Reset() { *m = AuthEnableResponse{} }
-func (m *AuthEnableResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthEnableResponse) ProtoMessage() {}
-func (*AuthEnableResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{80}
-}
-func (m *AuthEnableResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthEnableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthEnableResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthEnableResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthEnableResponse.Merge(m, src)
+func (x *AuthEnableResponse) Reset() {
+ *x = AuthEnableResponse{}
+ mi := &file_rpc_proto_msgTypes[80]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthEnableResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthEnableResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthEnableResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthEnableResponse.DiscardUnknown(m)
+
+func (*AuthEnableResponse) ProtoMessage() {}
+
+func (x *AuthEnableResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[80]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthEnableResponse proto.InternalMessageInfo
+// Deprecated: Use AuthEnableResponse.ProtoReflect.Descriptor instead.
+func (*AuthEnableResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{80}
+}
-func (m *AuthEnableResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthEnableResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type AuthDisableResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthDisableResponse) Reset() { *m = AuthDisableResponse{} }
-func (m *AuthDisableResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthDisableResponse) ProtoMessage() {}
-func (*AuthDisableResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{81}
-}
-func (m *AuthDisableResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthDisableResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthDisableResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthDisableResponse) Reset() {
+ *x = AuthDisableResponse{}
+ mi := &file_rpc_proto_msgTypes[81]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthDisableResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthDisableResponse.Merge(m, src)
-}
-func (m *AuthDisableResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthDisableResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthDisableResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthDisableResponse.DiscardUnknown(m)
+
+func (*AuthDisableResponse) ProtoMessage() {}
+
+func (x *AuthDisableResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[81]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthDisableResponse proto.InternalMessageInfo
+// Deprecated: Use AuthDisableResponse.ProtoReflect.Descriptor instead.
+func (*AuthDisableResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{81}
+}
-func (m *AuthDisableResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthDisableResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type AuthStatusResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"`
// authRevision is the current revision of auth store
- AuthRevision uint64 `protobuf:"varint,3,opt,name=authRevision,proto3" json:"authRevision,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ AuthRevision uint64 `protobuf:"varint,3,opt,name=authRevision,proto3" json:"authRevision,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthStatusResponse) Reset() { *m = AuthStatusResponse{} }
-func (m *AuthStatusResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthStatusResponse) ProtoMessage() {}
-func (*AuthStatusResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{82}
-}
-func (m *AuthStatusResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthStatusResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthStatusResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthStatusResponse.Merge(m, src)
+func (x *AuthStatusResponse) Reset() {
+ *x = AuthStatusResponse{}
+ mi := &file_rpc_proto_msgTypes[82]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthStatusResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthStatusResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthStatusResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthStatusResponse.DiscardUnknown(m)
+
+func (*AuthStatusResponse) ProtoMessage() {}
+
+func (x *AuthStatusResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[82]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthStatusResponse proto.InternalMessageInfo
+// Deprecated: Use AuthStatusResponse.ProtoReflect.Descriptor instead.
+func (*AuthStatusResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{82}
+}
-func (m *AuthStatusResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthStatusResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *AuthStatusResponse) GetEnabled() bool {
- if m != nil {
- return m.Enabled
+func (x *AuthStatusResponse) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
}
return false
}
-func (m *AuthStatusResponse) GetAuthRevision() uint64 {
- if m != nil {
- return m.AuthRevision
+func (x *AuthStatusResponse) GetAuthRevision() uint64 {
+ if x != nil {
+ return x.AuthRevision
}
return 0
}
type AuthenticateResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// token is an authorized token that can be used in succeeding RPCs
- Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthenticateResponse) Reset() { *m = AuthenticateResponse{} }
-func (m *AuthenticateResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthenticateResponse) ProtoMessage() {}
-func (*AuthenticateResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{83}
-}
-func (m *AuthenticateResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthenticateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthenticateResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthenticateResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthenticateResponse.Merge(m, src)
+func (x *AuthenticateResponse) Reset() {
+ *x = AuthenticateResponse{}
+ mi := &file_rpc_proto_msgTypes[83]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthenticateResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthenticateResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthenticateResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthenticateResponse.DiscardUnknown(m)
+
+func (*AuthenticateResponse) ProtoMessage() {}
+
+func (x *AuthenticateResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[83]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthenticateResponse proto.InternalMessageInfo
+// Deprecated: Use AuthenticateResponse.ProtoReflect.Descriptor instead.
+func (*AuthenticateResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{83}
+}
-func (m *AuthenticateResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthenticateResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *AuthenticateResponse) GetToken() string {
- if m != nil {
- return m.Token
+func (x *AuthenticateResponse) GetToken() string {
+ if x != nil {
+ return x.Token
}
return ""
}
type AuthUserAddResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserAddResponse) Reset() { *m = AuthUserAddResponse{} }
-func (m *AuthUserAddResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserAddResponse) ProtoMessage() {}
-func (*AuthUserAddResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{84}
-}
-func (m *AuthUserAddResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserAddResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
+func (x *AuthUserAddResponse) Reset() {
+ *x = AuthUserAddResponse{}
+ mi := &file_rpc_proto_msgTypes[84]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserAddResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserAddResponse.Merge(m, src)
-}
-func (m *AuthUserAddResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserAddResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserAddResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserAddResponse.DiscardUnknown(m)
+
+func (*AuthUserAddResponse) ProtoMessage() {}
+
+func (x *AuthUserAddResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[84]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserAddResponse proto.InternalMessageInfo
+// Deprecated: Use AuthUserAddResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserAddResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{84}
+}
-func (m *AuthUserAddResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserAddResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
type AuthUserGetResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserGetResponse) Reset() { *m = AuthUserGetResponse{} }
-func (m *AuthUserGetResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserGetResponse) ProtoMessage() {}
-func (*AuthUserGetResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{85}
-}
-func (m *AuthUserGetResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserGetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserGetResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthUserGetResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserGetResponse.Merge(m, src)
+func (x *AuthUserGetResponse) Reset() {
+ *x = AuthUserGetResponse{}
+ mi := &file_rpc_proto_msgTypes[85]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserGetResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserGetResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *AuthUserGetResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserGetResponse.DiscardUnknown(m)
+
+func (*AuthUserGetResponse) ProtoMessage() {}
+
+func (x *AuthUserGetResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[85]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_AuthUserGetResponse proto.InternalMessageInfo
+// Deprecated: Use AuthUserGetResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserGetResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{85}
+}
-func (m *AuthUserGetResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserGetResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *AuthUserGetResponse) GetRoles() []string {
- if m != nil {
- return m.Roles
+func (x *AuthUserGetResponse) GetRoles() []string {
+ if x != nil {
+ return x.Roles
}
return nil
}
type AuthUserDeleteResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserDeleteResponse) Reset() { *m = AuthUserDeleteResponse{} }
-func (m *AuthUserDeleteResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserDeleteResponse) ProtoMessage() {}
-func (*AuthUserDeleteResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{86}
-}
-func (m *AuthUserDeleteResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserDeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserDeleteResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthUserDeleteResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserDeleteResponse.Merge(m, src)
-}
-func (m *AuthUserDeleteResponse) XXX_Size() int {
- return m.Size()
+func (x *AuthUserDeleteResponse) Reset() {
+ *x = AuthUserDeleteResponse{}
+ mi := &file_rpc_proto_msgTypes[86]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserDeleteResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserDeleteResponse.DiscardUnknown(m)
+
+func (x *AuthUserDeleteResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthUserDeleteResponse proto.InternalMessageInfo
+func (*AuthUserDeleteResponse) ProtoMessage() {}
-func (m *AuthUserDeleteResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserDeleteResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[86]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type AuthUserChangePasswordResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use AuthUserDeleteResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserDeleteResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{86}
}
-func (m *AuthUserChangePasswordResponse) Reset() { *m = AuthUserChangePasswordResponse{} }
-func (m *AuthUserChangePasswordResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserChangePasswordResponse) ProtoMessage() {}
-func (*AuthUserChangePasswordResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{87}
-}
-func (m *AuthUserChangePasswordResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserChangePasswordResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserChangePasswordResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *AuthUserDeleteResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
+ return nil
}
-func (m *AuthUserChangePasswordResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserChangePasswordResponse.Merge(m, src)
+
+type AuthUserChangePasswordResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserChangePasswordResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserChangePasswordResponse) Reset() {
+ *x = AuthUserChangePasswordResponse{}
+ mi := &file_rpc_proto_msgTypes[87]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserChangePasswordResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserChangePasswordResponse.DiscardUnknown(m)
+
+func (x *AuthUserChangePasswordResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthUserChangePasswordResponse proto.InternalMessageInfo
+func (*AuthUserChangePasswordResponse) ProtoMessage() {}
-func (m *AuthUserChangePasswordResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserChangePasswordResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[87]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type AuthUserGrantRoleResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use AuthUserChangePasswordResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserChangePasswordResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{87}
}
-func (m *AuthUserGrantRoleResponse) Reset() { *m = AuthUserGrantRoleResponse{} }
-func (m *AuthUserGrantRoleResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserGrantRoleResponse) ProtoMessage() {}
-func (*AuthUserGrantRoleResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{88}
-}
-func (m *AuthUserGrantRoleResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserGrantRoleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserGrantRoleResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *AuthUserChangePasswordResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
+ return nil
}
-func (m *AuthUserGrantRoleResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserGrantRoleResponse.Merge(m, src)
+
+type AuthUserGrantRoleResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserGrantRoleResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserGrantRoleResponse) Reset() {
+ *x = AuthUserGrantRoleResponse{}
+ mi := &file_rpc_proto_msgTypes[88]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserGrantRoleResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserGrantRoleResponse.DiscardUnknown(m)
+
+func (x *AuthUserGrantRoleResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthUserGrantRoleResponse proto.InternalMessageInfo
+func (*AuthUserGrantRoleResponse) ProtoMessage() {}
-func (m *AuthUserGrantRoleResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserGrantRoleResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[88]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type AuthUserRevokeRoleResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use AuthUserGrantRoleResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserGrantRoleResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{88}
}
-func (m *AuthUserRevokeRoleResponse) Reset() { *m = AuthUserRevokeRoleResponse{} }
-func (m *AuthUserRevokeRoleResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserRevokeRoleResponse) ProtoMessage() {}
-func (*AuthUserRevokeRoleResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{89}
-}
-func (m *AuthUserRevokeRoleResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserRevokeRoleResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserRevokeRoleResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *AuthUserGrantRoleResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
+ return nil
}
-func (m *AuthUserRevokeRoleResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserRevokeRoleResponse.Merge(m, src)
+
+type AuthUserRevokeRoleResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthUserRevokeRoleResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthUserRevokeRoleResponse) Reset() {
+ *x = AuthUserRevokeRoleResponse{}
+ mi := &file_rpc_proto_msgTypes[89]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserRevokeRoleResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserRevokeRoleResponse.DiscardUnknown(m)
+
+func (x *AuthUserRevokeRoleResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthUserRevokeRoleResponse proto.InternalMessageInfo
+func (*AuthUserRevokeRoleResponse) ProtoMessage() {}
-func (m *AuthUserRevokeRoleResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthUserRevokeRoleResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[89]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type AuthRoleAddResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use AuthUserRevokeRoleResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserRevokeRoleResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{89}
}
-func (m *AuthRoleAddResponse) Reset() { *m = AuthRoleAddResponse{} }
-func (m *AuthRoleAddResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleAddResponse) ProtoMessage() {}
-func (*AuthRoleAddResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{90}
-}
-func (m *AuthRoleAddResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleAddResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleAddResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *AuthUserRevokeRoleResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
+ return nil
}
-func (m *AuthRoleAddResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleAddResponse.Merge(m, src)
+
+type AuthRoleAddResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleAddResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleAddResponse) Reset() {
+ *x = AuthRoleAddResponse{}
+ mi := &file_rpc_proto_msgTypes[90]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleAddResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleAddResponse.DiscardUnknown(m)
+
+func (x *AuthRoleAddResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthRoleAddResponse proto.InternalMessageInfo
+func (*AuthRoleAddResponse) ProtoMessage() {}
-func (m *AuthRoleAddResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthRoleAddResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[90]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type AuthRoleGetResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Perm []*authpb.Permission `protobuf:"bytes,2,rep,name=perm,proto3" json:"perm,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use AuthRoleAddResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleAddResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{90}
}
-func (m *AuthRoleGetResponse) Reset() { *m = AuthRoleGetResponse{} }
-func (m *AuthRoleGetResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleGetResponse) ProtoMessage() {}
-func (*AuthRoleGetResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{91}
-}
-func (m *AuthRoleGetResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleGetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleGetResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *AuthRoleAddResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
+ return nil
}
-func (m *AuthRoleGetResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleGetResponse.Merge(m, src)
+
+type AuthRoleGetResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Perm []*authpb.Permission `protobuf:"bytes,2,rep,name=perm,proto3" json:"perm,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *AuthRoleGetResponse) XXX_Size() int {
- return m.Size()
+
+func (x *AuthRoleGetResponse) Reset() {
+ *x = AuthRoleGetResponse{}
+ mi := &file_rpc_proto_msgTypes[91]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthRoleGetResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleGetResponse.DiscardUnknown(m)
+
+func (x *AuthRoleGetResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_AuthRoleGetResponse proto.InternalMessageInfo
+func (*AuthRoleGetResponse) ProtoMessage() {}
-func (m *AuthRoleGetResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthRoleGetResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[91]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-func (m *AuthRoleGetResponse) GetPerm() []*authpb.Permission {
- if m != nil {
- return m.Perm
- }
- return nil
-}
-
-type AuthRoleListResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *AuthRoleListResponse) Reset() { *m = AuthRoleListResponse{} }
-func (m *AuthRoleListResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleListResponse) ProtoMessage() {}
-func (*AuthRoleListResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{92}
-}
-func (m *AuthRoleListResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleListResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthRoleListResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleListResponse.Merge(m, src)
-}
-func (m *AuthRoleListResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthRoleListResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleListResponse.DiscardUnknown(m)
+// Deprecated: Use AuthRoleGetResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleGetResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{91}
}
-var xxx_messageInfo_AuthRoleListResponse proto.InternalMessageInfo
-
-func (m *AuthRoleListResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthRoleGetResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *AuthRoleListResponse) GetRoles() []string {
- if m != nil {
- return m.Roles
+func (x *AuthRoleGetResponse) GetPerm() []*authpb.Permission {
+ if x != nil {
+ return x.Perm
}
return nil
}
-type AuthUserListResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- Users []string `protobuf:"bytes,2,rep,name=users,proto3" json:"users,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *AuthUserListResponse) Reset() { *m = AuthUserListResponse{} }
-func (m *AuthUserListResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthUserListResponse) ProtoMessage() {}
-func (*AuthUserListResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{93}
-}
-func (m *AuthUserListResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthUserListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthUserListResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthUserListResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthUserListResponse.Merge(m, src)
-}
-func (m *AuthUserListResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthUserListResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthUserListResponse.DiscardUnknown(m)
+type AuthRoleListResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-var xxx_messageInfo_AuthUserListResponse proto.InternalMessageInfo
-
-func (m *AuthUserListResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
- }
- return nil
+func (x *AuthRoleListResponse) Reset() {
+ *x = AuthRoleListResponse{}
+ mi := &file_rpc_proto_msgTypes[92]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *AuthUserListResponse) GetUsers() []string {
- if m != nil {
- return m.Users
- }
- return nil
+func (x *AuthRoleListResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-type AuthRoleDeleteResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
+func (*AuthRoleListResponse) ProtoMessage() {}
-func (m *AuthRoleDeleteResponse) Reset() { *m = AuthRoleDeleteResponse{} }
-func (m *AuthRoleDeleteResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleDeleteResponse) ProtoMessage() {}
-func (*AuthRoleDeleteResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{94}
-}
-func (m *AuthRoleDeleteResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleDeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleDeleteResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *AuthRoleListResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[92]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
- }
-}
-func (m *AuthRoleDeleteResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleDeleteResponse.Merge(m, src)
-}
-func (m *AuthRoleDeleteResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthRoleDeleteResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleDeleteResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AuthRoleDeleteResponse proto.InternalMessageInfo
-
-func (m *AuthRoleDeleteResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+ return ms
}
- return nil
-}
-
-type AuthRoleGrantPermissionResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ return mi.MessageOf(x)
}
-func (m *AuthRoleGrantPermissionResponse) Reset() { *m = AuthRoleGrantPermissionResponse{} }
-func (m *AuthRoleGrantPermissionResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleGrantPermissionResponse) ProtoMessage() {}
-func (*AuthRoleGrantPermissionResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{95}
-}
-func (m *AuthRoleGrantPermissionResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleGrantPermissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleGrantPermissionResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthRoleGrantPermissionResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleGrantPermissionResponse.Merge(m, src)
-}
-func (m *AuthRoleGrantPermissionResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthRoleGrantPermissionResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleGrantPermissionResponse.DiscardUnknown(m)
+// Deprecated: Use AuthRoleListResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleListResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{92}
}
-var xxx_messageInfo_AuthRoleGrantPermissionResponse proto.InternalMessageInfo
-
-func (m *AuthRoleGrantPermissionResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthRoleListResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-type AuthRoleRevokePermissionResponse struct {
- Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *AuthRoleRevokePermissionResponse) Reset() { *m = AuthRoleRevokePermissionResponse{} }
-func (m *AuthRoleRevokePermissionResponse) String() string { return proto.CompactTextString(m) }
-func (*AuthRoleRevokePermissionResponse) ProtoMessage() {}
-func (*AuthRoleRevokePermissionResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_77a6da22d6a3feb1, []int{96}
-}
-func (m *AuthRoleRevokePermissionResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *AuthRoleRevokePermissionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_AuthRoleRevokePermissionResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *AuthRoleRevokePermissionResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_AuthRoleRevokePermissionResponse.Merge(m, src)
-}
-func (m *AuthRoleRevokePermissionResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *AuthRoleRevokePermissionResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_AuthRoleRevokePermissionResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AuthRoleRevokePermissionResponse proto.InternalMessageInfo
-
-func (m *AuthRoleRevokePermissionResponse) GetHeader() *ResponseHeader {
- if m != nil {
- return m.Header
+func (x *AuthRoleListResponse) GetRoles() []string {
+ if x != nil {
+ return x.Roles
}
return nil
}
-func init() {
- proto.RegisterEnum("etcdserverpb.AlarmType", AlarmType_name, AlarmType_value)
- proto.RegisterEnum("etcdserverpb.RangeRequest_SortOrder", RangeRequest_SortOrder_name, RangeRequest_SortOrder_value)
- proto.RegisterEnum("etcdserverpb.RangeRequest_SortTarget", RangeRequest_SortTarget_name, RangeRequest_SortTarget_value)
- proto.RegisterEnum("etcdserverpb.Compare_CompareResult", Compare_CompareResult_name, Compare_CompareResult_value)
- proto.RegisterEnum("etcdserverpb.Compare_CompareTarget", Compare_CompareTarget_name, Compare_CompareTarget_value)
- proto.RegisterEnum("etcdserverpb.WatchCreateRequest_FilterType", WatchCreateRequest_FilterType_name, WatchCreateRequest_FilterType_value)
- proto.RegisterEnum("etcdserverpb.AlarmRequest_AlarmAction", AlarmRequest_AlarmAction_name, AlarmRequest_AlarmAction_value)
- proto.RegisterEnum("etcdserverpb.DowngradeRequest_DowngradeAction", DowngradeRequest_DowngradeAction_name, DowngradeRequest_DowngradeAction_value)
- proto.RegisterType((*ResponseHeader)(nil), "etcdserverpb.ResponseHeader")
- proto.RegisterType((*RangeRequest)(nil), "etcdserverpb.RangeRequest")
- proto.RegisterType((*RangeResponse)(nil), "etcdserverpb.RangeResponse")
- proto.RegisterType((*PutRequest)(nil), "etcdserverpb.PutRequest")
- proto.RegisterType((*PutResponse)(nil), "etcdserverpb.PutResponse")
- proto.RegisterType((*DeleteRangeRequest)(nil), "etcdserverpb.DeleteRangeRequest")
- proto.RegisterType((*DeleteRangeResponse)(nil), "etcdserverpb.DeleteRangeResponse")
- proto.RegisterType((*RequestOp)(nil), "etcdserverpb.RequestOp")
- proto.RegisterType((*ResponseOp)(nil), "etcdserverpb.ResponseOp")
- proto.RegisterType((*Compare)(nil), "etcdserverpb.Compare")
- proto.RegisterType((*TxnRequest)(nil), "etcdserverpb.TxnRequest")
- proto.RegisterType((*TxnResponse)(nil), "etcdserverpb.TxnResponse")
- proto.RegisterType((*CompactionRequest)(nil), "etcdserverpb.CompactionRequest")
- proto.RegisterType((*CompactionResponse)(nil), "etcdserverpb.CompactionResponse")
- proto.RegisterType((*HashRequest)(nil), "etcdserverpb.HashRequest")
- proto.RegisterType((*HashKVRequest)(nil), "etcdserverpb.HashKVRequest")
- proto.RegisterType((*HashKVResponse)(nil), "etcdserverpb.HashKVResponse")
- proto.RegisterType((*HashResponse)(nil), "etcdserverpb.HashResponse")
- proto.RegisterType((*SnapshotRequest)(nil), "etcdserverpb.SnapshotRequest")
- proto.RegisterType((*SnapshotResponse)(nil), "etcdserverpb.SnapshotResponse")
- proto.RegisterType((*WatchRequest)(nil), "etcdserverpb.WatchRequest")
- proto.RegisterType((*WatchCreateRequest)(nil), "etcdserverpb.WatchCreateRequest")
- proto.RegisterType((*WatchCancelRequest)(nil), "etcdserverpb.WatchCancelRequest")
- proto.RegisterType((*WatchProgressRequest)(nil), "etcdserverpb.WatchProgressRequest")
- proto.RegisterType((*WatchResponse)(nil), "etcdserverpb.WatchResponse")
- proto.RegisterType((*LeaseGrantRequest)(nil), "etcdserverpb.LeaseGrantRequest")
- proto.RegisterType((*LeaseGrantResponse)(nil), "etcdserverpb.LeaseGrantResponse")
- proto.RegisterType((*LeaseRevokeRequest)(nil), "etcdserverpb.LeaseRevokeRequest")
- proto.RegisterType((*LeaseRevokeResponse)(nil), "etcdserverpb.LeaseRevokeResponse")
- proto.RegisterType((*LeaseCheckpoint)(nil), "etcdserverpb.LeaseCheckpoint")
- proto.RegisterType((*LeaseCheckpointRequest)(nil), "etcdserverpb.LeaseCheckpointRequest")
- proto.RegisterType((*LeaseCheckpointResponse)(nil), "etcdserverpb.LeaseCheckpointResponse")
- proto.RegisterType((*LeaseKeepAliveRequest)(nil), "etcdserverpb.LeaseKeepAliveRequest")
- proto.RegisterType((*LeaseKeepAliveResponse)(nil), "etcdserverpb.LeaseKeepAliveResponse")
- proto.RegisterType((*LeaseTimeToLiveRequest)(nil), "etcdserverpb.LeaseTimeToLiveRequest")
- proto.RegisterType((*LeaseTimeToLiveResponse)(nil), "etcdserverpb.LeaseTimeToLiveResponse")
- proto.RegisterType((*LeaseLeasesRequest)(nil), "etcdserverpb.LeaseLeasesRequest")
- proto.RegisterType((*LeaseStatus)(nil), "etcdserverpb.LeaseStatus")
- proto.RegisterType((*LeaseLeasesResponse)(nil), "etcdserverpb.LeaseLeasesResponse")
- proto.RegisterType((*Member)(nil), "etcdserverpb.Member")
- proto.RegisterType((*MemberAddRequest)(nil), "etcdserverpb.MemberAddRequest")
- proto.RegisterType((*MemberAddResponse)(nil), "etcdserverpb.MemberAddResponse")
- proto.RegisterType((*MemberRemoveRequest)(nil), "etcdserverpb.MemberRemoveRequest")
- proto.RegisterType((*MemberRemoveResponse)(nil), "etcdserverpb.MemberRemoveResponse")
- proto.RegisterType((*MemberUpdateRequest)(nil), "etcdserverpb.MemberUpdateRequest")
- proto.RegisterType((*MemberUpdateResponse)(nil), "etcdserverpb.MemberUpdateResponse")
- proto.RegisterType((*MemberListRequest)(nil), "etcdserverpb.MemberListRequest")
- proto.RegisterType((*MemberListResponse)(nil), "etcdserverpb.MemberListResponse")
- proto.RegisterType((*MemberPromoteRequest)(nil), "etcdserverpb.MemberPromoteRequest")
- proto.RegisterType((*MemberPromoteResponse)(nil), "etcdserverpb.MemberPromoteResponse")
- proto.RegisterType((*DefragmentRequest)(nil), "etcdserverpb.DefragmentRequest")
- proto.RegisterType((*DefragmentResponse)(nil), "etcdserverpb.DefragmentResponse")
- proto.RegisterType((*MoveLeaderRequest)(nil), "etcdserverpb.MoveLeaderRequest")
- proto.RegisterType((*MoveLeaderResponse)(nil), "etcdserverpb.MoveLeaderResponse")
- proto.RegisterType((*AlarmRequest)(nil), "etcdserverpb.AlarmRequest")
- proto.RegisterType((*AlarmMember)(nil), "etcdserverpb.AlarmMember")
- proto.RegisterType((*AlarmResponse)(nil), "etcdserverpb.AlarmResponse")
- proto.RegisterType((*DowngradeRequest)(nil), "etcdserverpb.DowngradeRequest")
- proto.RegisterType((*DowngradeResponse)(nil), "etcdserverpb.DowngradeResponse")
- proto.RegisterType((*DowngradeVersionTestRequest)(nil), "etcdserverpb.DowngradeVersionTestRequest")
- proto.RegisterType((*StatusRequest)(nil), "etcdserverpb.StatusRequest")
- proto.RegisterType((*StatusResponse)(nil), "etcdserverpb.StatusResponse")
- proto.RegisterType((*DowngradeInfo)(nil), "etcdserverpb.DowngradeInfo")
- proto.RegisterType((*AuthEnableRequest)(nil), "etcdserverpb.AuthEnableRequest")
- proto.RegisterType((*AuthDisableRequest)(nil), "etcdserverpb.AuthDisableRequest")
- proto.RegisterType((*AuthStatusRequest)(nil), "etcdserverpb.AuthStatusRequest")
- proto.RegisterType((*AuthenticateRequest)(nil), "etcdserverpb.AuthenticateRequest")
- proto.RegisterType((*AuthUserAddRequest)(nil), "etcdserverpb.AuthUserAddRequest")
- proto.RegisterType((*AuthUserGetRequest)(nil), "etcdserverpb.AuthUserGetRequest")
- proto.RegisterType((*AuthUserDeleteRequest)(nil), "etcdserverpb.AuthUserDeleteRequest")
- proto.RegisterType((*AuthUserChangePasswordRequest)(nil), "etcdserverpb.AuthUserChangePasswordRequest")
- proto.RegisterType((*AuthUserGrantRoleRequest)(nil), "etcdserverpb.AuthUserGrantRoleRequest")
- proto.RegisterType((*AuthUserRevokeRoleRequest)(nil), "etcdserverpb.AuthUserRevokeRoleRequest")
- proto.RegisterType((*AuthRoleAddRequest)(nil), "etcdserverpb.AuthRoleAddRequest")
- proto.RegisterType((*AuthRoleGetRequest)(nil), "etcdserverpb.AuthRoleGetRequest")
- proto.RegisterType((*AuthUserListRequest)(nil), "etcdserverpb.AuthUserListRequest")
- proto.RegisterType((*AuthRoleListRequest)(nil), "etcdserverpb.AuthRoleListRequest")
- proto.RegisterType((*AuthRoleDeleteRequest)(nil), "etcdserverpb.AuthRoleDeleteRequest")
- proto.RegisterType((*AuthRoleGrantPermissionRequest)(nil), "etcdserverpb.AuthRoleGrantPermissionRequest")
- proto.RegisterType((*AuthRoleRevokePermissionRequest)(nil), "etcdserverpb.AuthRoleRevokePermissionRequest")
- proto.RegisterType((*AuthEnableResponse)(nil), "etcdserverpb.AuthEnableResponse")
- proto.RegisterType((*AuthDisableResponse)(nil), "etcdserverpb.AuthDisableResponse")
- proto.RegisterType((*AuthStatusResponse)(nil), "etcdserverpb.AuthStatusResponse")
- proto.RegisterType((*AuthenticateResponse)(nil), "etcdserverpb.AuthenticateResponse")
- proto.RegisterType((*AuthUserAddResponse)(nil), "etcdserverpb.AuthUserAddResponse")
- proto.RegisterType((*AuthUserGetResponse)(nil), "etcdserverpb.AuthUserGetResponse")
- proto.RegisterType((*AuthUserDeleteResponse)(nil), "etcdserverpb.AuthUserDeleteResponse")
- proto.RegisterType((*AuthUserChangePasswordResponse)(nil), "etcdserverpb.AuthUserChangePasswordResponse")
- proto.RegisterType((*AuthUserGrantRoleResponse)(nil), "etcdserverpb.AuthUserGrantRoleResponse")
- proto.RegisterType((*AuthUserRevokeRoleResponse)(nil), "etcdserverpb.AuthUserRevokeRoleResponse")
- proto.RegisterType((*AuthRoleAddResponse)(nil), "etcdserverpb.AuthRoleAddResponse")
- proto.RegisterType((*AuthRoleGetResponse)(nil), "etcdserverpb.AuthRoleGetResponse")
- proto.RegisterType((*AuthRoleListResponse)(nil), "etcdserverpb.AuthRoleListResponse")
- proto.RegisterType((*AuthUserListResponse)(nil), "etcdserverpb.AuthUserListResponse")
- proto.RegisterType((*AuthRoleDeleteResponse)(nil), "etcdserverpb.AuthRoleDeleteResponse")
- proto.RegisterType((*AuthRoleGrantPermissionResponse)(nil), "etcdserverpb.AuthRoleGrantPermissionResponse")
- proto.RegisterType((*AuthRoleRevokePermissionResponse)(nil), "etcdserverpb.AuthRoleRevokePermissionResponse")
-}
-
-func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) }
-
-var fileDescriptor_77a6da22d6a3feb1 = []byte{
- // 4574 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3c, 0x5d, 0x6f, 0x1b, 0x57,
- 0x76, 0x1a, 0x92, 0x12, 0xc9, 0xc3, 0x0f, 0xd1, 0xd7, 0xb2, 0x4d, 0xd3, 0xb6, 0xac, 0x8c, 0xed,
- 0xc4, 0x71, 0x62, 0xd1, 0x96, 0xec, 0x64, 0xeb, 0x22, 0xe9, 0xd2, 0x12, 0x63, 0x6b, 0x2d, 0x4b,
- 0xca, 0x88, 0x76, 0x36, 0x2e, 0xb0, 0xea, 0x88, 0xbc, 0xa6, 0x66, 0x45, 0xce, 0x70, 0x67, 0x86,
- 0xb4, 0x94, 0x3e, 0x6c, 0xba, 0xed, 0x76, 0xb1, 0x2d, 0xb0, 0x40, 0x53, 0xa0, 0x58, 0x14, 0xed,
- 0x4b, 0x5b, 0xa0, 0x7d, 0x68, 0x8b, 0xf6, 0xa1, 0x0f, 0x45, 0x0b, 0xf4, 0xa1, 0x7d, 0x68, 0x1f,
- 0x0a, 0x14, 0xe8, 0x1f, 0x68, 0xd3, 0x7d, 0xea, 0xaf, 0x58, 0xdc, 0xaf, 0xb9, 0x77, 0xbe, 0x24,
- 0x67, 0xa5, 0x60, 0x5f, 0x62, 0xce, 0x3d, 0x9f, 0xf7, 0x9c, 0x7b, 0xcf, 0xb9, 0xf7, 0x9c, 0x1b,
- 0x41, 0xd1, 0x1d, 0x75, 0x17, 0x47, 0xae, 0xe3, 0x3b, 0xa8, 0x8c, 0xfd, 0x6e, 0xcf, 0xc3, 0xee,
- 0x04, 0xbb, 0xa3, 0xdd, 0xc6, 0x5c, 0xdf, 0xe9, 0x3b, 0x14, 0xd0, 0x24, 0xbf, 0x18, 0x4e, 0xa3,
- 0x4e, 0x70, 0x9a, 0xe6, 0xc8, 0x6a, 0x0e, 0x27, 0xdd, 0xee, 0x68, 0xb7, 0xb9, 0x3f, 0xe1, 0x90,
- 0x46, 0x00, 0x31, 0xc7, 0xfe, 0xde, 0x68, 0x97, 0xfe, 0xc3, 0x61, 0x0b, 0x01, 0x6c, 0x82, 0x5d,
- 0xcf, 0x72, 0xec, 0xd1, 0xae, 0xf8, 0xc5, 0x31, 0x2e, 0xf7, 0x1d, 0xa7, 0x3f, 0xc0, 0x8c, 0xde,
- 0xb6, 0x1d, 0xdf, 0xf4, 0x2d, 0xc7, 0xf6, 0x38, 0x94, 0xfd, 0xd3, 0xbd, 0xdd, 0xc7, 0xf6, 0x6d,
- 0x67, 0x84, 0x6d, 0x73, 0x64, 0x4d, 0x96, 0x9a, 0xce, 0x88, 0xe2, 0xc4, 0xf1, 0xf5, 0x9f, 0x68,
- 0x50, 0x35, 0xb0, 0x37, 0x72, 0x6c, 0x0f, 0x3f, 0xc6, 0x66, 0x0f, 0xbb, 0xe8, 0x0a, 0x40, 0x77,
- 0x30, 0xf6, 0x7c, 0xec, 0xee, 0x58, 0xbd, 0xba, 0xb6, 0xa0, 0xdd, 0xcc, 0x19, 0x45, 0x3e, 0xb2,
- 0xd6, 0x43, 0x97, 0xa0, 0x38, 0xc4, 0xc3, 0x5d, 0x06, 0xcd, 0x50, 0x68, 0x81, 0x0d, 0xac, 0xf5,
- 0x50, 0x03, 0x0a, 0x2e, 0x9e, 0x58, 0x44, 0xdd, 0x7a, 0x76, 0x41, 0xbb, 0x99, 0x35, 0x82, 0x6f,
- 0x42, 0xe8, 0x9a, 0x2f, 0xfd, 0x1d, 0x1f, 0xbb, 0xc3, 0x7a, 0x8e, 0x11, 0x92, 0x81, 0x0e, 0x76,
- 0x87, 0x0f, 0xf2, 0x3f, 0xf8, 0x87, 0x7a, 0x76, 0x79, 0xf1, 0x8e, 0xfe, 0xaf, 0xd3, 0x50, 0x36,
- 0x4c, 0xbb, 0x8f, 0x0d, 0xfc, 0xbd, 0x31, 0xf6, 0x7c, 0x54, 0x83, 0xec, 0x3e, 0x3e, 0xa4, 0x7a,
- 0x94, 0x0d, 0xf2, 0x93, 0x31, 0xb2, 0xfb, 0x78, 0x07, 0xdb, 0x4c, 0x83, 0x32, 0x61, 0x64, 0xf7,
- 0x71, 0xdb, 0xee, 0xa1, 0x39, 0x98, 0x1e, 0x58, 0x43, 0xcb, 0xe7, 0xe2, 0xd9, 0x47, 0x48, 0xaf,
- 0x5c, 0x44, 0xaf, 0x15, 0x00, 0xcf, 0x71, 0xfd, 0x1d, 0xc7, 0xed, 0x61, 0xb7, 0x3e, 0xbd, 0xa0,
- 0xdd, 0xac, 0x2e, 0x5d, 0x5f, 0x54, 0x3d, 0xbc, 0xa8, 0x2a, 0xb4, 0xb8, 0xed, 0xb8, 0xfe, 0x26,
- 0xc1, 0x35, 0x8a, 0x9e, 0xf8, 0x89, 0x3e, 0x82, 0x12, 0x65, 0xe2, 0x9b, 0x6e, 0x1f, 0xfb, 0xf5,
- 0x19, 0xca, 0xe5, 0xc6, 0x31, 0x5c, 0x3a, 0x14, 0xd9, 0xa0, 0xe2, 0xd9, 0x6f, 0xa4, 0x43, 0xd9,
- 0xc3, 0xae, 0x65, 0x0e, 0xac, 0xcf, 0xcc, 0xdd, 0x01, 0xae, 0xe7, 0x17, 0xb4, 0x9b, 0x05, 0x23,
- 0x34, 0x46, 0xe6, 0xbf, 0x8f, 0x0f, 0xbd, 0x1d, 0xc7, 0x1e, 0x1c, 0xd6, 0x0b, 0x14, 0xa1, 0x40,
- 0x06, 0x36, 0xed, 0xc1, 0x21, 0xf5, 0x9e, 0x33, 0xb6, 0x7d, 0x06, 0x2d, 0x52, 0x68, 0x91, 0x8e,
- 0x50, 0xf0, 0x5d, 0xa8, 0x0d, 0x2d, 0x7b, 0x67, 0xe8, 0xf4, 0x76, 0x02, 0x83, 0x00, 0x31, 0xc8,
- 0xc3, 0xfc, 0xef, 0x51, 0x0f, 0xdc, 0x35, 0xaa, 0x43, 0xcb, 0x7e, 0xea, 0xf4, 0x0c, 0x61, 0x1f,
- 0x42, 0x62, 0x1e, 0x84, 0x49, 0x4a, 0x51, 0x12, 0xf3, 0x40, 0x25, 0x79, 0x1f, 0xce, 0x12, 0x29,
- 0x5d, 0x17, 0x9b, 0x3e, 0x96, 0x54, 0xe5, 0x30, 0xd5, 0x99, 0xa1, 0x65, 0xaf, 0x50, 0x94, 0x10,
- 0xa1, 0x79, 0x10, 0x23, 0xac, 0x44, 0x09, 0xcd, 0x83, 0x30, 0xa1, 0xfe, 0x3e, 0x14, 0x03, 0xbf,
- 0xa0, 0x02, 0xe4, 0x36, 0x36, 0x37, 0xda, 0xb5, 0x29, 0x04, 0x30, 0xd3, 0xda, 0x5e, 0x69, 0x6f,
- 0xac, 0xd6, 0x34, 0x54, 0x82, 0xfc, 0x6a, 0x9b, 0x7d, 0x64, 0x1a, 0xf9, 0x2f, 0xf8, 0x7a, 0x7b,
- 0x02, 0x20, 0x5d, 0x81, 0xf2, 0x90, 0x7d, 0xd2, 0xfe, 0xb4, 0x36, 0x45, 0x90, 0x9f, 0xb7, 0x8d,
- 0xed, 0xb5, 0xcd, 0x8d, 0x9a, 0x46, 0xb8, 0xac, 0x18, 0xed, 0x56, 0xa7, 0x5d, 0xcb, 0x10, 0x8c,
- 0xa7, 0x9b, 0xab, 0xb5, 0x2c, 0x2a, 0xc2, 0xf4, 0xf3, 0xd6, 0xfa, 0xb3, 0x76, 0x2d, 0x17, 0x30,
- 0x93, 0xab, 0xf8, 0x4f, 0x34, 0xa8, 0x70, 0x77, 0xb3, 0xbd, 0x85, 0xee, 0xc1, 0xcc, 0x1e, 0xdd,
- 0x5f, 0x74, 0x25, 0x97, 0x96, 0x2e, 0x47, 0xd6, 0x46, 0x68, 0x0f, 0x1a, 0x1c, 0x17, 0xe9, 0x90,
- 0xdd, 0x9f, 0x78, 0xf5, 0xcc, 0x42, 0xf6, 0x66, 0x69, 0xa9, 0xb6, 0xc8, 0x22, 0xc9, 0xe2, 0x13,
- 0x7c, 0xf8, 0xdc, 0x1c, 0x8c, 0xb1, 0x41, 0x80, 0x08, 0x41, 0x6e, 0xe8, 0xb8, 0x98, 0x2e, 0xf8,
- 0x82, 0x41, 0x7f, 0x93, 0x5d, 0x40, 0x7d, 0xce, 0x17, 0x3b, 0xfb, 0x90, 0xea, 0xfd, 0xa7, 0x06,
- 0xb0, 0x35, 0xf6, 0xd3, 0xb7, 0xd8, 0x1c, 0x4c, 0x4f, 0x88, 0x04, 0xbe, 0xbd, 0xd8, 0x07, 0xdd,
- 0x5b, 0xd8, 0xf4, 0x70, 0xb0, 0xb7, 0xc8, 0x07, 0x5a, 0x80, 0xfc, 0xc8, 0xc5, 0x93, 0x9d, 0xfd,
- 0x09, 0x95, 0x56, 0x90, 0x7e, 0x9a, 0x21, 0xe3, 0x4f, 0x26, 0xe8, 0x16, 0x94, 0xad, 0xbe, 0xed,
- 0xb8, 0x78, 0x87, 0x31, 0x9d, 0x56, 0xd1, 0x96, 0x8c, 0x12, 0x03, 0xd2, 0x29, 0x29, 0xb8, 0x4c,
- 0xd4, 0x4c, 0x22, 0xee, 0x3a, 0x81, 0xc9, 0xf9, 0x7c, 0xae, 0x41, 0x89, 0xce, 0xe7, 0x44, 0xc6,
- 0x5e, 0x92, 0x13, 0xc9, 0x50, 0xb2, 0x98, 0xc1, 0x63, 0x53, 0x93, 0x2a, 0xd8, 0x80, 0x56, 0xf1,
- 0x00, 0xfb, 0xf8, 0x24, 0xc1, 0x4b, 0x31, 0x65, 0x36, 0xd1, 0x94, 0x52, 0xde, 0x5f, 0x68, 0x70,
- 0x36, 0x24, 0xf0, 0x44, 0x53, 0xaf, 0x43, 0xbe, 0x47, 0x99, 0x31, 0x9d, 0xb2, 0x86, 0xf8, 0x44,
- 0xf7, 0xa0, 0xc0, 0x55, 0xf2, 0xea, 0xd9, 0xe4, 0x65, 0x28, 0xb5, 0xcc, 0x33, 0x2d, 0x3d, 0xa9,
- 0xe6, 0x3f, 0x65, 0xa0, 0xc8, 0x8d, 0xb1, 0x39, 0x42, 0x2d, 0xa8, 0xb8, 0xec, 0x63, 0x87, 0xce,
- 0x99, 0xeb, 0xd8, 0x48, 0x8f, 0x93, 0x8f, 0xa7, 0x8c, 0x32, 0x27, 0xa1, 0xc3, 0xe8, 0x57, 0xa1,
- 0x24, 0x58, 0x8c, 0xc6, 0x3e, 0x77, 0x54, 0x3d, 0xcc, 0x40, 0x2e, 0xed, 0xc7, 0x53, 0x06, 0x70,
- 0xf4, 0xad, 0xb1, 0x8f, 0x3a, 0x30, 0x27, 0x88, 0xd9, 0xfc, 0xb8, 0x1a, 0x59, 0xca, 0x65, 0x21,
- 0xcc, 0x25, 0xee, 0xce, 0xc7, 0x53, 0x06, 0xe2, 0xf4, 0x0a, 0x10, 0xad, 0x4a, 0x95, 0xfc, 0x03,
- 0x96, 0x5f, 0x62, 0x2a, 0x75, 0x0e, 0x6c, 0xce, 0x44, 0x58, 0x6b, 0x59, 0xd1, 0xad, 0x73, 0x60,
- 0x07, 0x26, 0x7b, 0x58, 0x84, 0x3c, 0x1f, 0xd6, 0xff, 0x23, 0x03, 0x20, 0x3c, 0xb6, 0x39, 0x42,
- 0xab, 0x50, 0x75, 0xf9, 0x57, 0xc8, 0x7e, 0x97, 0x12, 0xed, 0xc7, 0x1d, 0x3d, 0x65, 0x54, 0x04,
- 0x11, 0x53, 0xf7, 0x43, 0x28, 0x07, 0x5c, 0xa4, 0x09, 0x2f, 0x26, 0x98, 0x30, 0xe0, 0x50, 0x12,
- 0x04, 0xc4, 0x88, 0x9f, 0xc0, 0xb9, 0x80, 0x3e, 0xc1, 0x8a, 0x6f, 0x1c, 0x61, 0xc5, 0x80, 0xe1,
- 0x59, 0xc1, 0x41, 0xb5, 0xe3, 0x23, 0x45, 0x31, 0x69, 0xc8, 0x8b, 0x09, 0x86, 0x64, 0x48, 0xaa,
- 0x25, 0x03, 0x0d, 0x43, 0xa6, 0x04, 0x92, 0xf6, 0xd9, 0xb8, 0xfe, 0x57, 0x39, 0xc8, 0xaf, 0x38,
- 0xc3, 0x91, 0xe9, 0x92, 0x45, 0x34, 0xe3, 0x62, 0x6f, 0x3c, 0xf0, 0xa9, 0x01, 0xab, 0x4b, 0xd7,
- 0xc2, 0x32, 0x38, 0x9a, 0xf8, 0xd7, 0xa0, 0xa8, 0x06, 0x27, 0x21, 0xc4, 0x3c, 0xcb, 0x67, 0x5e,
- 0x83, 0x98, 0xe7, 0x78, 0x4e, 0x22, 0x02, 0x42, 0x56, 0x06, 0x84, 0x06, 0xe4, 0xf9, 0x01, 0x8f,
- 0x05, 0xeb, 0xc7, 0x53, 0x86, 0x18, 0x40, 0x6f, 0xc3, 0x6c, 0x34, 0x15, 0x4e, 0x73, 0x9c, 0x6a,
- 0x37, 0x9c, 0x39, 0xaf, 0x41, 0x39, 0x94, 0xa1, 0x67, 0x38, 0x5e, 0x69, 0xa8, 0xe4, 0xe5, 0xf3,
- 0x22, 0xac, 0x93, 0x63, 0x45, 0xf9, 0xf1, 0x94, 0x08, 0xec, 0x57, 0x45, 0x60, 0x2f, 0xa8, 0x89,
- 0x96, 0xd8, 0x95, 0xc7, 0xf8, 0xeb, 0x6a, 0xd4, 0xfa, 0x26, 0x21, 0x0e, 0x90, 0x64, 0xf8, 0xd2,
- 0x0d, 0xa8, 0x84, 0x4c, 0x46, 0x72, 0x64, 0xfb, 0xe3, 0x67, 0xad, 0x75, 0x96, 0x50, 0x1f, 0xd1,
- 0x1c, 0x6a, 0xd4, 0x34, 0x92, 0xa0, 0xd7, 0xdb, 0xdb, 0xdb, 0xb5, 0x0c, 0x3a, 0x0f, 0xc5, 0x8d,
- 0xcd, 0xce, 0x0e, 0xc3, 0xca, 0x36, 0xf2, 0x7f, 0xcc, 0x22, 0x89, 0xcc, 0xcf, 0x9f, 0x06, 0x3c,
- 0x79, 0x8a, 0x56, 0x32, 0xf3, 0x94, 0x92, 0x99, 0x35, 0x91, 0x99, 0x33, 0x32, 0x33, 0x67, 0x11,
- 0x82, 0xe9, 0xf5, 0x76, 0x6b, 0x9b, 0x26, 0x69, 0xc6, 0x7a, 0x39, 0x9e, 0xad, 0x1f, 0x56, 0xa1,
- 0xcc, 0xdc, 0xb3, 0x33, 0xb6, 0xc9, 0x61, 0xe2, 0xaf, 0x35, 0x00, 0xb9, 0x61, 0x51, 0x13, 0xf2,
- 0x5d, 0xa6, 0x42, 0x5d, 0xa3, 0x11, 0xf0, 0x5c, 0xa2, 0xc7, 0x0d, 0x81, 0x85, 0xee, 0x42, 0xde,
- 0x1b, 0x77, 0xbb, 0xd8, 0x13, 0x99, 0xfb, 0x42, 0x34, 0x08, 0xf3, 0x80, 0x68, 0x08, 0x3c, 0x42,
- 0xf2, 0xd2, 0xb4, 0x06, 0x63, 0x9a, 0xc7, 0x8f, 0x26, 0xe1, 0x78, 0x32, 0xc6, 0xfe, 0x99, 0x06,
- 0x25, 0x65, 0x5b, 0xfc, 0x82, 0x29, 0xe0, 0x32, 0x14, 0xa9, 0x32, 0xb8, 0xc7, 0x93, 0x40, 0xc1,
- 0x90, 0x03, 0xe8, 0x3d, 0x28, 0x8a, 0x9d, 0x24, 0xf2, 0x40, 0x3d, 0x99, 0xed, 0xe6, 0xc8, 0x90,
- 0xa8, 0x52, 0xc9, 0x0e, 0x9c, 0xa1, 0x76, 0xea, 0x92, 0xdb, 0x87, 0xb0, 0xac, 0x7a, 0x2c, 0xd7,
- 0x22, 0xc7, 0xf2, 0x06, 0x14, 0x46, 0x7b, 0x87, 0x9e, 0xd5, 0x35, 0x07, 0x5c, 0x9d, 0xe0, 0x5b,
- 0x72, 0xdd, 0x06, 0xa4, 0x72, 0x3d, 0x89, 0x01, 0x24, 0xd3, 0xf3, 0x50, 0x7a, 0x6c, 0x7a, 0x7b,
- 0x5c, 0x49, 0x39, 0x7e, 0x0f, 0x2a, 0x64, 0xfc, 0xc9, 0xf3, 0xd7, 0x50, 0x5f, 0x50, 0x2d, 0xeb,
- 0xff, 0xac, 0x41, 0x55, 0x90, 0x9d, 0xc8, 0x41, 0x08, 0x72, 0x7b, 0xa6, 0xb7, 0x47, 0x8d, 0x51,
- 0x31, 0xe8, 0x6f, 0xf4, 0x36, 0xd4, 0xba, 0x6c, 0xfe, 0x3b, 0x91, 0x7b, 0xd7, 0x2c, 0x1f, 0x0f,
- 0xf6, 0xfe, 0xbb, 0x50, 0x21, 0x24, 0x3b, 0xe1, 0x7b, 0x90, 0xd8, 0xc6, 0xef, 0x19, 0xe5, 0x3d,
- 0x3a, 0xe7, 0xa8, 0xfa, 0x26, 0x94, 0x99, 0x31, 0x4e, 0x5b, 0x77, 0x69, 0xd7, 0x06, 0xcc, 0x6e,
- 0xdb, 0xe6, 0xc8, 0xdb, 0x73, 0xfc, 0x88, 0xcd, 0x97, 0xf5, 0xbf, 0xd7, 0xa0, 0x26, 0x81, 0x27,
- 0xd2, 0xe1, 0x2d, 0x98, 0x75, 0xf1, 0xd0, 0xb4, 0x6c, 0xcb, 0xee, 0xef, 0xec, 0x1e, 0xfa, 0xd8,
- 0xe3, 0xd7, 0xd7, 0x6a, 0x30, 0xfc, 0x90, 0x8c, 0x12, 0x65, 0x77, 0x07, 0xce, 0x2e, 0x0f, 0xd2,
- 0xf4, 0x37, 0x7a, 0x23, 0x1c, 0xa5, 0x8b, 0xd2, 0x6e, 0x62, 0x5c, 0xea, 0xfc, 0xd3, 0x0c, 0x94,
- 0x3f, 0x31, 0xfd, 0xae, 0x58, 0x41, 0x68, 0x0d, 0xaa, 0x41, 0x18, 0xa7, 0x23, 0x5c, 0xef, 0xc8,
- 0x81, 0x83, 0xd2, 0x88, 0x7b, 0x8d, 0x38, 0x70, 0x54, 0xba, 0xea, 0x00, 0x65, 0x65, 0xda, 0x5d,
- 0x3c, 0x08, 0x58, 0x65, 0xd2, 0x59, 0x51, 0x44, 0x95, 0x95, 0x3a, 0x80, 0xbe, 0x0d, 0xb5, 0x91,
- 0xeb, 0xf4, 0x5d, 0xec, 0x79, 0x01, 0x33, 0x96, 0xc2, 0xf5, 0x04, 0x66, 0x5b, 0x1c, 0x35, 0x72,
- 0x8a, 0xb9, 0xf7, 0x78, 0xca, 0x98, 0x1d, 0x85, 0x61, 0x32, 0xb0, 0xce, 0xca, 0xf3, 0x1e, 0x8b,
- 0xac, 0x3f, 0xca, 0x02, 0x8a, 0x4f, 0xf3, 0xab, 0x1e, 0x93, 0x6f, 0x40, 0xd5, 0xf3, 0x4d, 0x37,
- 0xb6, 0xe6, 0x2b, 0x74, 0x34, 0x58, 0xf1, 0x6f, 0x41, 0xa0, 0xd9, 0x8e, 0xed, 0xf8, 0xd6, 0xcb,
- 0x43, 0x76, 0x41, 0x31, 0xaa, 0x62, 0x78, 0x83, 0x8e, 0xa2, 0x0d, 0xc8, 0xbf, 0xb4, 0x06, 0x3e,
- 0x76, 0xbd, 0xfa, 0xf4, 0x42, 0xf6, 0x66, 0x75, 0xe9, 0x9d, 0xe3, 0x1c, 0xb3, 0xf8, 0x11, 0xc5,
- 0xef, 0x1c, 0x8e, 0xd4, 0xd3, 0x2f, 0x67, 0xa2, 0x1e, 0xe3, 0x67, 0x92, 0x6f, 0x44, 0x3a, 0x14,
- 0x5e, 0x11, 0xa6, 0x3b, 0x56, 0x8f, 0xe6, 0xe2, 0x60, 0x1f, 0xde, 0x33, 0xf2, 0x14, 0xb0, 0xd6,
- 0x43, 0xd7, 0xa0, 0xf0, 0xd2, 0x35, 0xfb, 0x43, 0x6c, 0xfb, 0xec, 0x96, 0x2f, 0x71, 0x02, 0x80,
- 0xbe, 0x08, 0x20, 0x55, 0x21, 0x99, 0x6f, 0x63, 0x73, 0xeb, 0x59, 0xa7, 0x36, 0x85, 0xca, 0x50,
- 0xd8, 0xd8, 0x5c, 0x6d, 0xaf, 0xb7, 0x49, 0x6e, 0x14, 0x39, 0xef, 0xae, 0xdc, 0x74, 0x2d, 0xe1,
- 0x88, 0xd0, 0x9a, 0x50, 0xf5, 0xd2, 0xc2, 0x97, 0x6e, 0xa1, 0x97, 0x60, 0x71, 0x57, 0xbf, 0x0a,
- 0x73, 0x49, 0x4b, 0x43, 0x20, 0xdc, 0xd3, 0xff, 0x2d, 0x03, 0x15, 0xbe, 0x11, 0x4e, 0xb4, 0x73,
- 0x2f, 0x2a, 0x5a, 0xf1, 0xeb, 0x89, 0x30, 0x52, 0x1d, 0xf2, 0x6c, 0x83, 0xf4, 0xf8, 0xfd, 0x57,
- 0x7c, 0x92, 0xe0, 0xcc, 0xd6, 0x3b, 0xee, 0x71, 0xb7, 0x07, 0xdf, 0x89, 0x61, 0x73, 0x3a, 0x35,
- 0x6c, 0x06, 0x1b, 0xce, 0xf4, 0xf8, 0xc1, 0xaa, 0x28, 0x5d, 0x51, 0x16, 0x9b, 0x8a, 0x00, 0x43,
- 0x3e, 0xcb, 0xa7, 0xf8, 0x0c, 0xdd, 0x80, 0x19, 0x3c, 0xc1, 0xb6, 0xef, 0xd5, 0x4b, 0x34, 0x91,
- 0x56, 0xc4, 0x85, 0xaa, 0x4d, 0x46, 0x0d, 0x0e, 0x94, 0xae, 0xfa, 0x10, 0xce, 0xd0, 0xfb, 0xee,
- 0x23, 0xd7, 0xb4, 0xd5, 0x3b, 0x7b, 0xa7, 0xb3, 0xce, 0xd3, 0x0e, 0xf9, 0x89, 0xaa, 0x90, 0x59,
- 0x5b, 0xe5, 0xf6, 0xc9, 0xac, 0xad, 0x4a, 0xfa, 0xdf, 0xd7, 0x00, 0xa9, 0x0c, 0x4e, 0xe4, 0x8b,
- 0x88, 0x14, 0xa1, 0x47, 0x56, 0xea, 0x31, 0x07, 0xd3, 0xd8, 0x75, 0x1d, 0x97, 0x05, 0x4a, 0x83,
- 0x7d, 0x48, 0x6d, 0x6e, 0x73, 0x65, 0x0c, 0x3c, 0x71, 0xf6, 0x83, 0x08, 0xc0, 0xd8, 0x6a, 0x71,
- 0xe5, 0x3b, 0x70, 0x36, 0x84, 0x7e, 0x3a, 0x29, 0x7e, 0x13, 0x66, 0x29, 0xd7, 0x95, 0x3d, 0xdc,
- 0xdd, 0x1f, 0x39, 0x96, 0x1d, 0xd3, 0x00, 0x5d, 0x23, 0xb1, 0x4b, 0xa4, 0x0b, 0x32, 0x45, 0x36,
- 0xe7, 0x72, 0x30, 0xd8, 0xe9, 0xac, 0xcb, 0xa5, 0xbe, 0x0b, 0xe7, 0x23, 0x0c, 0xc5, 0xcc, 0x7e,
- 0x0d, 0x4a, 0xdd, 0x60, 0xd0, 0xe3, 0x27, 0xc8, 0x2b, 0x61, 0x75, 0xa3, 0xa4, 0x2a, 0x85, 0x94,
- 0xf1, 0x6d, 0xb8, 0x10, 0x93, 0x71, 0x1a, 0xe6, 0xb8, 0xa7, 0xdf, 0x81, 0x73, 0x94, 0xf3, 0x13,
- 0x8c, 0x47, 0xad, 0x81, 0x35, 0x39, 0xde, 0x2d, 0x87, 0x7c, 0xbe, 0x0a, 0xc5, 0xd7, 0xbb, 0xac,
- 0xa4, 0xe8, 0x36, 0x17, 0xdd, 0xb1, 0x86, 0xb8, 0xe3, 0xac, 0xa7, 0x6b, 0x4b, 0x12, 0xf9, 0x3e,
- 0x3e, 0xf4, 0xf8, 0xf1, 0x91, 0xfe, 0x96, 0xd1, 0xeb, 0x6f, 0x35, 0x6e, 0x4e, 0x95, 0xcf, 0xd7,
- 0xbc, 0x35, 0xe6, 0x01, 0xfa, 0x64, 0x0f, 0xe2, 0x1e, 0x01, 0xb0, 0xda, 0x9c, 0x32, 0x12, 0x28,
- 0x4c, 0xb2, 0x50, 0x39, 0xaa, 0xf0, 0x15, 0xbe, 0x71, 0xe8, 0x7f, 0xbc, 0xd8, 0x49, 0xe9, 0x4d,
- 0x28, 0x51, 0xc8, 0xb6, 0x6f, 0xfa, 0x63, 0x2f, 0xcd, 0x73, 0xcb, 0xfa, 0x8f, 0x34, 0xbe, 0xa3,
- 0x04, 0x9f, 0x13, 0xcd, 0xf9, 0x2e, 0xcc, 0xd0, 0x1b, 0xa2, 0xb8, 0xe9, 0x5c, 0x4c, 0x58, 0xd8,
- 0x4c, 0x23, 0x83, 0x23, 0x2a, 0xe7, 0x24, 0x0d, 0x66, 0x9e, 0xd2, 0xce, 0x81, 0xa2, 0x6d, 0x4e,
- 0x78, 0xce, 0x36, 0x87, 0xac, 0xfc, 0x58, 0x34, 0xe8, 0x6f, 0x7a, 0x21, 0xc0, 0xd8, 0x7d, 0x66,
- 0xac, 0xb3, 0x1b, 0x48, 0xd1, 0x08, 0xbe, 0x89, 0x61, 0xbb, 0x03, 0x0b, 0xdb, 0x3e, 0x85, 0xe6,
- 0x28, 0x54, 0x19, 0x41, 0x37, 0xa0, 0x68, 0x79, 0xeb, 0xd8, 0x74, 0x6d, 0x5e, 0xe2, 0x57, 0x02,
- 0xb3, 0x84, 0xc8, 0x35, 0xf6, 0x1d, 0xa8, 0x31, 0xcd, 0x5a, 0xbd, 0x9e, 0x72, 0xda, 0x0f, 0xe4,
- 0x6b, 0x11, 0xf9, 0x21, 0xfe, 0x99, 0xe3, 0xf9, 0xff, 0x9d, 0x06, 0x67, 0x14, 0x01, 0x27, 0x72,
- 0xc1, 0xbb, 0x30, 0xc3, 0xfa, 0x2f, 0xfc, 0x28, 0x38, 0x17, 0xa6, 0x62, 0x62, 0x0c, 0x8e, 0x83,
- 0x16, 0x21, 0xcf, 0x7e, 0x89, 0x6b, 0x5c, 0x32, 0xba, 0x40, 0x92, 0x2a, 0x2f, 0xc2, 0x59, 0x0e,
- 0xc3, 0x43, 0x27, 0x69, 0xcf, 0xe5, 0xc2, 0x11, 0xe2, 0x87, 0x1a, 0xcc, 0x85, 0x09, 0x4e, 0x34,
- 0x4b, 0x45, 0xef, 0xcc, 0x57, 0xd2, 0xfb, 0x5b, 0x42, 0xef, 0x67, 0xa3, 0x9e, 0x72, 0xe4, 0x8c,
- 0xae, 0x38, 0xd5, 0xbb, 0x99, 0xb0, 0x77, 0x25, 0xaf, 0x9f, 0x04, 0x73, 0x12, 0xcc, 0x4e, 0x34,
- 0xa7, 0xf7, 0x5f, 0x6b, 0x4e, 0xca, 0x11, 0x2c, 0x36, 0xb9, 0x35, 0xb1, 0x8c, 0xd6, 0x2d, 0x2f,
- 0xc8, 0x38, 0xef, 0x40, 0x79, 0x60, 0xd9, 0xd8, 0x74, 0x79, 0x0f, 0x49, 0x53, 0xd7, 0xe3, 0x7d,
- 0x23, 0x04, 0x94, 0xac, 0x7e, 0x5b, 0x03, 0xa4, 0xf2, 0xfa, 0xe5, 0x78, 0xab, 0x29, 0x0c, 0xbc,
- 0xe5, 0x3a, 0x43, 0xc7, 0x3f, 0x6e, 0x99, 0xdd, 0xd3, 0x7f, 0x57, 0x83, 0x73, 0x11, 0x8a, 0x5f,
- 0x86, 0xe6, 0xf7, 0xf4, 0xcb, 0x70, 0x66, 0x15, 0x8b, 0x33, 0x5e, 0xac, 0x76, 0xb0, 0x0d, 0x48,
- 0x85, 0x9e, 0xce, 0x29, 0xe6, 0x1b, 0x70, 0xe6, 0xa9, 0x33, 0x21, 0x81, 0x9c, 0x80, 0x65, 0x98,
- 0x62, 0xc5, 0xac, 0xc0, 0x5e, 0xc1, 0xb7, 0x0c, 0xbd, 0xdb, 0x80, 0x54, 0xca, 0xd3, 0x50, 0x67,
- 0x59, 0xff, 0x5f, 0x0d, 0xca, 0xad, 0x81, 0xe9, 0x0e, 0x85, 0x2a, 0x1f, 0xc2, 0x0c, 0xab, 0xcc,
- 0xf0, 0x32, 0xeb, 0x9b, 0x61, 0x7e, 0x2a, 0x2e, 0xfb, 0x68, 0xb1, 0x3a, 0x0e, 0xa7, 0x22, 0x53,
- 0xe1, 0x9d, 0xe5, 0xd5, 0x48, 0xa7, 0x79, 0x15, 0xdd, 0x86, 0x69, 0x93, 0x90, 0xd0, 0xf4, 0x5a,
- 0x8d, 0x96, 0xcb, 0x28, 0x37, 0x72, 0x25, 0x32, 0x18, 0x96, 0xfe, 0x01, 0x94, 0x14, 0x09, 0x28,
- 0x0f, 0xd9, 0x47, 0x6d, 0x7e, 0x4d, 0x6a, 0xad, 0x74, 0xd6, 0x9e, 0xb3, 0x12, 0x62, 0x15, 0x60,
- 0xb5, 0x1d, 0x7c, 0x67, 0x12, 0x1a, 0x7b, 0x26, 0xe7, 0xc3, 0xf3, 0x96, 0xaa, 0xa1, 0x96, 0xa6,
- 0x61, 0xe6, 0x75, 0x34, 0x94, 0x22, 0x7e, 0x4b, 0x83, 0x0a, 0x37, 0xcd, 0x49, 0x53, 0x33, 0xe5,
- 0x9c, 0x92, 0x9a, 0x95, 0x69, 0x18, 0x1c, 0x51, 0xea, 0xf0, 0x2f, 0x1a, 0xd4, 0x56, 0x9d, 0x57,
- 0x76, 0xdf, 0x35, 0x7b, 0xc1, 0x1e, 0xfc, 0x28, 0xe2, 0xce, 0xc5, 0x48, 0xa5, 0x3f, 0x82, 0x2f,
- 0x07, 0x22, 0x6e, 0xad, 0xcb, 0x5a, 0x0a, 0xcb, 0xef, 0xe2, 0x53, 0xff, 0x26, 0xcc, 0x46, 0x88,
- 0x88, 0x83, 0x9e, 0xb7, 0xd6, 0xd7, 0x56, 0x89, 0x43, 0x68, 0xbd, 0xb7, 0xbd, 0xd1, 0x7a, 0xb8,
- 0xde, 0xe6, 0x5d, 0xd9, 0xd6, 0xc6, 0x4a, 0x7b, 0x5d, 0x3a, 0xea, 0xbe, 0x98, 0xc1, 0x7d, 0x7d,
- 0x00, 0x67, 0x14, 0x85, 0x4e, 0xda, 0x1c, 0x4b, 0xd6, 0x57, 0x4a, 0xfb, 0x06, 0x5c, 0x0a, 0xa4,
- 0x3d, 0x67, 0xc0, 0x0e, 0xf6, 0xd4, 0xcb, 0xda, 0x84, 0x0b, 0x2d, 0x1a, 0xe4, 0xa7, 0xa0, 0x7c,
- 0x4f, 0xaf, 0x43, 0x85, 0x9f, 0x8f, 0xa2, 0x21, 0xe3, 0xcf, 0x73, 0x50, 0x15, 0xa0, 0xaf, 0x47,
- 0x7f, 0x74, 0x1e, 0x66, 0x7a, 0xbb, 0xdb, 0xd6, 0x67, 0xa2, 0xa3, 0xcb, 0xbf, 0xc8, 0xf8, 0x80,
- 0xc9, 0x61, 0xef, 0x34, 0xf8, 0x17, 0xba, 0xcc, 0x9e, 0x70, 0xac, 0xd9, 0x3d, 0x7c, 0x40, 0x8f,
- 0x51, 0x39, 0x43, 0x0e, 0xd0, 0x72, 0x28, 0x7f, 0xcf, 0x41, 0x6f, 0xc9, 0xca, 0xfb, 0x0e, 0xb4,
- 0x0c, 0x35, 0xf2, 0xbb, 0x35, 0x1a, 0x0d, 0x2c, 0xdc, 0x63, 0x0c, 0xc8, 0x05, 0x39, 0x27, 0xcf,
- 0x49, 0x31, 0x04, 0x74, 0x15, 0x66, 0xe8, 0xe5, 0xd1, 0xab, 0x17, 0x48, 0x46, 0x96, 0xa8, 0x7c,
- 0x18, 0xbd, 0x0d, 0x25, 0xa6, 0xf1, 0x9a, 0xfd, 0xcc, 0xc3, 0xf4, 0xb5, 0x83, 0x52, 0x49, 0x51,
- 0x61, 0xe1, 0x13, 0x1a, 0xa4, 0x9d, 0xd0, 0x50, 0x13, 0xaa, 0x9e, 0xef, 0xb8, 0x66, 0x5f, 0xb8,
- 0x91, 0x3e, 0x75, 0x50, 0xca, 0x7d, 0x11, 0xb0, 0x54, 0xe1, 0xe3, 0xb1, 0xe3, 0x9b, 0xe1, 0x27,
- 0x0e, 0xef, 0x19, 0x2a, 0x0c, 0x7d, 0x0b, 0x2a, 0x3d, 0xb1, 0x48, 0xd6, 0xec, 0x97, 0x0e, 0x7d,
- 0xd6, 0x10, 0xeb, 0xde, 0xad, 0xaa, 0x28, 0x92, 0x53, 0x98, 0x54, 0xbd, 0xc9, 0x56, 0x42, 0x14,
- 0xc4, 0xdb, 0xd8, 0x26, 0xa9, 0x9d, 0x55, 0x70, 0x0a, 0x86, 0xf8, 0x44, 0xd7, 0xa1, 0xc2, 0x32,
- 0xc1, 0xf3, 0xd0, 0x6a, 0x08, 0x0f, 0x92, 0x3c, 0xd6, 0x1a, 0xfb, 0x7b, 0x6d, 0x4a, 0x14, 0x5b,
- 0x94, 0x57, 0x00, 0x11, 0xe8, 0xaa, 0xe5, 0x25, 0x82, 0x39, 0x71, 0xe2, 0x8a, 0xbe, 0xaf, 0x6f,
- 0xc0, 0x59, 0x02, 0xc5, 0xb6, 0x6f, 0x75, 0x95, 0xa3, 0x98, 0x38, 0xec, 0x6b, 0x91, 0xc3, 0xbe,
- 0xe9, 0x79, 0xaf, 0x1c, 0xb7, 0xc7, 0xd5, 0x0c, 0xbe, 0xa5, 0xb4, 0x7f, 0xd4, 0x98, 0x36, 0xcf,
- 0xbc, 0xd0, 0x41, 0xfd, 0x2b, 0xf2, 0x43, 0xbf, 0x02, 0x79, 0xfe, 0x40, 0x8a, 0xd7, 0x3f, 0xcf,
- 0x2f, 0xb2, 0x87, 0x59, 0x8b, 0x9c, 0xf1, 0x26, 0x83, 0x2a, 0x35, 0x3a, 0x8e, 0x4f, 0x96, 0xcb,
- 0x9e, 0xe9, 0xed, 0xe1, 0xde, 0x96, 0x60, 0x1e, 0xaa, 0x0e, 0xdf, 0x37, 0x22, 0x60, 0xa9, 0xfb,
- 0x5d, 0xa9, 0xfa, 0x23, 0xec, 0x1f, 0xa1, 0xba, 0xda, 0x7f, 0x38, 0x27, 0x48, 0x78, 0xdb, 0xf4,
- 0x75, 0xa8, 0x7e, 0xac, 0xc1, 0x15, 0x41, 0xb6, 0xb2, 0x67, 0xda, 0x7d, 0x2c, 0x94, 0xf9, 0x45,
- 0xed, 0x15, 0x9f, 0x74, 0xf6, 0x35, 0x27, 0xfd, 0x04, 0xea, 0xc1, 0xa4, 0x69, 0x2d, 0xca, 0x19,
- 0xa8, 0x93, 0x18, 0x7b, 0x41, 0x90, 0xa4, 0xbf, 0xc9, 0x98, 0xeb, 0x0c, 0x82, 0x6b, 0x20, 0xf9,
- 0x2d, 0x99, 0xad, 0xc3, 0x45, 0xc1, 0x8c, 0x17, 0x87, 0xc2, 0xdc, 0x62, 0x73, 0x3a, 0x92, 0x1b,
- 0xf7, 0x07, 0xe1, 0x71, 0xf4, 0x52, 0x4a, 0x24, 0x09, 0xbb, 0x90, 0x4a, 0xd1, 0x92, 0xa4, 0xcc,
- 0xb3, 0x1d, 0x40, 0x74, 0x56, 0x4e, 0xec, 0x31, 0x38, 0x61, 0x99, 0x08, 0xe7, 0x4b, 0x80, 0xc0,
- 0x63, 0x4b, 0x20, 0x5d, 0x2a, 0x86, 0xf9, 0x40, 0x51, 0x62, 0xf6, 0x2d, 0xec, 0x0e, 0x2d, 0xcf,
- 0x53, 0x1a, 0x71, 0x49, 0xe6, 0x7a, 0x13, 0x72, 0x23, 0xcc, 0x8f, 0x2f, 0xa5, 0x25, 0x24, 0xf6,
- 0x84, 0x42, 0x4c, 0xe1, 0x52, 0xcc, 0x10, 0xae, 0x0a, 0x31, 0xcc, 0x21, 0x89, 0x72, 0xa2, 0x6a,
- 0x8a, 0xe2, 0x7f, 0x26, 0xa5, 0xf8, 0x9f, 0x0d, 0x17, 0xff, 0x43, 0x47, 0x6a, 0x35, 0x50, 0x9d,
- 0xce, 0x91, 0xba, 0xc3, 0x1c, 0x10, 0xc4, 0xb7, 0xd3, 0xe1, 0xfa, 0x07, 0x3c, 0x50, 0x9d, 0x56,
- 0x3a, 0x17, 0x01, 0x3e, 0x13, 0x0e, 0xf0, 0x3a, 0x94, 0x89, 0x93, 0x0c, 0xb5, 0x2b, 0x92, 0x33,
- 0x42, 0x63, 0x32, 0x18, 0xef, 0xc3, 0x5c, 0x38, 0x18, 0x9f, 0x48, 0xa9, 0x39, 0x98, 0xf6, 0x9d,
- 0x7d, 0x2c, 0x72, 0x0a, 0xfb, 0x88, 0x99, 0x35, 0x08, 0xd4, 0xa7, 0x63, 0xd6, 0xef, 0x4a, 0xae,
- 0x74, 0x03, 0x9e, 0x74, 0x06, 0x64, 0x39, 0x8a, 0xdb, 0x3f, 0xfb, 0x90, 0xb2, 0x3e, 0x81, 0xf3,
- 0xd1, 0xe0, 0x7b, 0x3a, 0x93, 0xd8, 0x61, 0x9b, 0x33, 0x29, 0x3c, 0x9f, 0x8e, 0x80, 0x17, 0x32,
- 0x4e, 0x2a, 0x41, 0xf7, 0x74, 0x78, 0xff, 0x3a, 0x34, 0x92, 0x62, 0xf0, 0xa9, 0xee, 0xc5, 0x20,
- 0x24, 0x9f, 0x0e, 0xd7, 0x1f, 0x6a, 0x92, 0xad, 0xba, 0x6a, 0x3e, 0xf8, 0x2a, 0x6c, 0x45, 0xae,
- 0xbb, 0x13, 0x2c, 0x9f, 0x66, 0x10, 0x2d, 0xb3, 0xc9, 0xd1, 0x52, 0x92, 0x50, 0x44, 0xb1, 0xff,
- 0x64, 0xa8, 0xff, 0x3a, 0x57, 0x2f, 0x17, 0x26, 0xf3, 0xce, 0x49, 0x85, 0x91, 0xf4, 0x1c, 0x08,
- 0xa3, 0x1f, 0xb1, 0xad, 0xa2, 0x26, 0xa9, 0xd3, 0x71, 0xdd, 0x6f, 0xc8, 0x04, 0x13, 0xcb, 0x63,
- 0xa7, 0x23, 0xc1, 0x84, 0x85, 0xf4, 0x14, 0x76, 0x2a, 0x22, 0x6e, 0xb5, 0xa0, 0x18, 0xdc, 0xfd,
- 0x95, 0x97, 0xca, 0x25, 0xc8, 0x6f, 0x6c, 0x6e, 0x6f, 0xb5, 0x56, 0xc8, 0xd5, 0x76, 0x0e, 0xf2,
- 0x2b, 0x9b, 0x86, 0xf1, 0x6c, 0xab, 0x43, 0xee, 0xb6, 0xd1, 0x87, 0x4b, 0x4b, 0x3f, 0xcb, 0x42,
- 0xe6, 0xc9, 0x73, 0xf4, 0x29, 0x4c, 0xb3, 0x87, 0x73, 0x47, 0xbc, 0x9f, 0x6c, 0x1c, 0xf5, 0x36,
- 0x50, 0xbf, 0xf0, 0x83, 0xff, 0xfe, 0xd9, 0x1f, 0x66, 0xce, 0xe8, 0xe5, 0xe6, 0x64, 0xb9, 0xb9,
- 0x3f, 0x69, 0xd2, 0x24, 0xfb, 0x40, 0xbb, 0x85, 0x3e, 0x86, 0xec, 0xd6, 0xd8, 0x47, 0xa9, 0xef,
- 0x2a, 0x1b, 0xe9, 0xcf, 0x05, 0xf5, 0x73, 0x94, 0xe9, 0xac, 0x0e, 0x9c, 0xe9, 0x68, 0xec, 0x13,
- 0x96, 0xdf, 0x83, 0x92, 0xfa, 0xd8, 0xef, 0xd8, 0xc7, 0x96, 0x8d, 0xe3, 0x1f, 0x12, 0xea, 0x57,
- 0xa8, 0xa8, 0x0b, 0x3a, 0xe2, 0xa2, 0xd8, 0x73, 0x44, 0x75, 0x16, 0x9d, 0x03, 0x1b, 0xa5, 0x3e,
- 0xc5, 0x6c, 0xa4, 0xbf, 0x2d, 0x8c, 0xcd, 0xc2, 0x3f, 0xb0, 0x09, 0xcb, 0xef, 0xf2, 0x47, 0x84,
- 0x5d, 0x1f, 0x5d, 0x4d, 0x78, 0x05, 0xa6, 0xbe, 0x6e, 0x6a, 0x2c, 0xa4, 0x23, 0x70, 0x21, 0x97,
- 0xa9, 0x90, 0xf3, 0xfa, 0x19, 0x2e, 0xa4, 0x1b, 0xa0, 0x3c, 0xd0, 0x6e, 0x2d, 0x75, 0x61, 0x9a,
- 0x76, 0xcf, 0xd1, 0x0b, 0xf1, 0xa3, 0x91, 0xf0, 0x2e, 0x21, 0xc5, 0xd1, 0xa1, 0xbe, 0xbb, 0x3e,
- 0x47, 0x05, 0x55, 0xf5, 0x22, 0x11, 0x44, 0x7b, 0xe7, 0x0f, 0xb4, 0x5b, 0x37, 0xb5, 0x3b, 0xda,
- 0xd2, 0xdf, 0x4c, 0xc3, 0x34, 0xed, 0xd2, 0xa0, 0x7d, 0x00, 0xd9, 0x25, 0x8e, 0xce, 0x2e, 0xd6,
- 0x80, 0x8e, 0xce, 0x2e, 0xde, 0x60, 0xd6, 0x1b, 0x54, 0xe8, 0x9c, 0x3e, 0x4b, 0x84, 0xd2, 0xe6,
- 0x4f, 0x93, 0xf6, 0xba, 0x88, 0x1d, 0x7f, 0xac, 0xf1, 0x76, 0x15, 0xdb, 0x66, 0x28, 0x89, 0x5b,
- 0xa8, 0x43, 0x1c, 0x5d, 0x0e, 0x09, 0x4d, 0x61, 0xfd, 0x3e, 0x15, 0xd8, 0xd4, 0x6b, 0x52, 0xa0,
- 0x4b, 0x31, 0x1e, 0x68, 0xb7, 0x5e, 0xd4, 0xf5, 0xb3, 0xdc, 0xca, 0x11, 0x08, 0xfa, 0x3e, 0x54,
- 0xc3, 0xbd, 0x4c, 0x74, 0x2d, 0x41, 0x56, 0xb4, 0x37, 0xda, 0xb8, 0x7e, 0x34, 0x12, 0xd7, 0x69,
- 0x9e, 0xea, 0xc4, 0x85, 0x33, 0xc9, 0xfb, 0x18, 0x8f, 0x4c, 0x82, 0xc4, 0x7d, 0x80, 0xfe, 0x54,
- 0xe3, 0xed, 0x68, 0xd9, 0x8a, 0x44, 0x49, 0xdc, 0x63, 0x1d, 0xcf, 0xc6, 0x8d, 0x63, 0xb0, 0xb8,
- 0x12, 0x1f, 0x50, 0x25, 0xde, 0xd7, 0xe7, 0xa4, 0x12, 0xbe, 0x35, 0xc4, 0xbe, 0xc3, 0xb5, 0x78,
- 0x71, 0x59, 0xbf, 0x10, 0x32, 0x4e, 0x08, 0x2a, 0x9d, 0xc5, 0x5a, 0x86, 0x89, 0xce, 0x0a, 0x75,
- 0x25, 0x13, 0x9d, 0x15, 0xee, 0x37, 0x26, 0x39, 0x8b, 0x37, 0x08, 0x13, 0x9c, 0x15, 0x40, 0x96,
- 0xfe, 0x3f, 0x07, 0xf9, 0x15, 0xf6, 0x3f, 0x23, 0x21, 0x07, 0x8a, 0x41, 0x13, 0x0d, 0xcd, 0x27,
- 0xd5, 0xe9, 0xe5, 0x55, 0xae, 0x71, 0x35, 0x15, 0xce, 0x15, 0x7a, 0x83, 0x2a, 0x74, 0x49, 0x3f,
- 0x4f, 0x24, 0xf3, 0xff, 0xdf, 0xa9, 0xc9, 0xaa, 0xb9, 0x4d, 0xb3, 0xd7, 0x23, 0x86, 0xf8, 0x4d,
- 0x28, 0xab, 0x2d, 0x2d, 0xf4, 0x46, 0x62, 0x6f, 0x40, 0xed, 0x8f, 0x35, 0xf4, 0xa3, 0x50, 0xb8,
- 0xe4, 0xeb, 0x54, 0xf2, 0xbc, 0x7e, 0x31, 0x41, 0xb2, 0x4b, 0x51, 0x43, 0xc2, 0x59, 0xef, 0x29,
- 0x59, 0x78, 0xa8, 0xc9, 0x95, 0x2c, 0x3c, 0xdc, 0xba, 0x3a, 0x52, 0xf8, 0x98, 0xa2, 0x12, 0xe1,
- 0x1e, 0x80, 0x6c, 0x0e, 0xa1, 0x44, 0x5b, 0x2a, 0x17, 0xd6, 0x68, 0x70, 0x88, 0xf7, 0x95, 0x74,
- 0x9d, 0x8a, 0xe5, 0xeb, 0x2e, 0x22, 0x76, 0x60, 0x79, 0x3e, 0xdb, 0x98, 0x95, 0x50, 0x6b, 0x07,
- 0x25, 0xce, 0x27, 0xdc, 0x29, 0x6a, 0x5c, 0x3b, 0x12, 0x87, 0x4b, 0xbf, 0x41, 0xa5, 0x5f, 0xd5,
- 0x1b, 0x09, 0xd2, 0x47, 0x0c, 0x97, 0x2c, 0xb6, 0xcf, 0xf3, 0x50, 0x7a, 0x6a, 0x5a, 0xb6, 0x8f,
- 0x6d, 0xd3, 0xee, 0x62, 0xb4, 0x0b, 0xd3, 0x34, 0x77, 0x47, 0x03, 0xb1, 0xda, 0xc9, 0x88, 0x06,
- 0xe2, 0x50, 0x29, 0x5f, 0x5f, 0xa0, 0x82, 0x1b, 0xfa, 0x39, 0x22, 0x78, 0x28, 0x59, 0x37, 0x59,
- 0x13, 0x40, 0xbb, 0x85, 0x5e, 0xc2, 0x0c, 0x6f, 0xe1, 0x47, 0x18, 0x85, 0x8a, 0x6a, 0x8d, 0xcb,
- 0xc9, 0xc0, 0xa4, 0xb5, 0xac, 0x8a, 0xf1, 0x28, 0x1e, 0x91, 0x33, 0x01, 0x90, 0x1d, 0xa9, 0xa8,
- 0x47, 0x63, 0x9d, 0xac, 0xc6, 0x42, 0x3a, 0x42, 0x92, 0x4d, 0x55, 0x99, 0xbd, 0x00, 0x97, 0xc8,
- 0xfd, 0x0e, 0xe4, 0x1e, 0x9b, 0xde, 0x1e, 0x8a, 0xe4, 0x5e, 0xe5, 0xc5, 0x6d, 0xa3, 0x91, 0x04,
- 0xe2, 0x52, 0xae, 0x52, 0x29, 0x17, 0x59, 0x28, 0x53, 0xa5, 0xd0, 0x37, 0xa5, 0xcc, 0x7e, 0xec,
- 0xb9, 0x6d, 0xd4, 0x7e, 0xa1, 0xb7, 0xbb, 0x51, 0xfb, 0x85, 0x5f, 0xe8, 0xa6, 0xdb, 0x8f, 0x48,
- 0xd9, 0x9f, 0x10, 0x39, 0x23, 0x28, 0x88, 0x87, 0xa9, 0x28, 0xf2, 0x9c, 0x27, 0xf2, 0x9a, 0xb5,
- 0x31, 0x9f, 0x06, 0xe6, 0xd2, 0xae, 0x51, 0x69, 0x57, 0xf4, 0x7a, 0xcc, 0x5b, 0x1c, 0xf3, 0x81,
- 0x76, 0xeb, 0x8e, 0x86, 0xbe, 0x0f, 0x20, 0x9b, 0x76, 0xb1, 0x3d, 0x18, 0x6d, 0x04, 0xc6, 0xf6,
- 0x60, 0xac, 0xdf, 0xa7, 0x2f, 0x52, 0xb9, 0x37, 0xf5, 0x6b, 0x51, 0xb9, 0xbe, 0x6b, 0xda, 0xde,
- 0x4b, 0xec, 0xde, 0x66, 0x75, 0x7f, 0x6f, 0xcf, 0x1a, 0x91, 0x29, 0xbb, 0x50, 0x0c, 0x6a, 0xcd,
- 0xd1, 0x78, 0x1b, 0xed, 0xfe, 0x44, 0xe3, 0x6d, 0xac, 0x19, 0x13, 0x0e, 0x3c, 0xa1, 0xf5, 0x22,
- 0x50, 0xc9, 0x16, 0xfc, 0xcb, 0x1a, 0xe4, 0xc8, 0x91, 0x9c, 0x1c, 0x4f, 0x64, 0xb9, 0x27, 0x3a,
- 0xfb, 0x58, 0xc5, 0x3a, 0x3a, 0xfb, 0x78, 0xa5, 0x28, 0x7c, 0x3c, 0x21, 0xd7, 0xb5, 0x26, 0xab,
- 0xa3, 0x90, 0x99, 0x3a, 0x50, 0x52, 0xca, 0x40, 0x28, 0x81, 0x59, 0xb8, 0x02, 0x1e, 0x4d, 0x78,
- 0x09, 0x35, 0x24, 0xfd, 0x12, 0x95, 0x77, 0x8e, 0x25, 0x3c, 0x2a, 0xaf, 0xc7, 0x30, 0x88, 0x40,
- 0x3e, 0x3b, 0xbe, 0xf3, 0x13, 0x66, 0x17, 0xde, 0xfd, 0x0b, 0xe9, 0x08, 0xa9, 0xb3, 0x93, 0x5b,
- 0xff, 0x15, 0x94, 0xd5, 0xd2, 0x0f, 0x4a, 0x50, 0x3e, 0x52, 0xa3, 0x8f, 0x66, 0x92, 0xa4, 0xca,
- 0x51, 0x38, 0xb6, 0x51, 0x91, 0xa6, 0x82, 0x46, 0x04, 0x0f, 0x20, 0xcf, 0x4b, 0x40, 0x49, 0x26,
- 0x0d, 0x97, 0xf1, 0x93, 0x4c, 0x1a, 0xa9, 0x1f, 0x85, 0xcf, 0xcf, 0x54, 0x22, 0xb9, 0x8a, 0x8a,
- 0x6c, 0xcd, 0xa5, 0x3d, 0xc2, 0x7e, 0x9a, 0x34, 0x59, 0xb6, 0x4d, 0x93, 0xa6, 0x54, 0x08, 0xd2,
- 0xa4, 0xf5, 0xb1, 0xcf, 0xe3, 0x81, 0xb8, 0x5e, 0xa3, 0x14, 0x66, 0x6a, 0x86, 0xd4, 0x8f, 0x42,
- 0x49, 0xba, 0xde, 0x48, 0x81, 0x22, 0x3d, 0x1e, 0x00, 0xc8, 0x72, 0x54, 0xf4, 0xcc, 0x9a, 0xd8,
- 0x29, 0x88, 0x9e, 0x59, 0x93, 0x2b, 0x5a, 0xe1, 0x18, 0x2b, 0xe5, 0xb2, 0xdb, 0x15, 0x91, 0xfc,
- 0x85, 0x06, 0x28, 0x5e, 0xb0, 0x42, 0xef, 0x24, 0x73, 0x4f, 0xec, 0x3a, 0x34, 0xde, 0x7d, 0x3d,
- 0xe4, 0xa4, 0x80, 0x2c, 0x55, 0xea, 0x52, 0xec, 0xd1, 0x2b, 0xa2, 0xd4, 0xe7, 0x1a, 0x54, 0x42,
- 0x45, 0x2e, 0xf4, 0x66, 0x8a, 0x4f, 0x23, 0xad, 0x87, 0xc6, 0x5b, 0xc7, 0xe2, 0x25, 0x1d, 0xe6,
- 0x95, 0x15, 0x20, 0x6e, 0x35, 0xbf, 0xa3, 0x41, 0x35, 0x5c, 0x0b, 0x43, 0x29, 0xbc, 0x63, 0x1d,
- 0x8b, 0xc6, 0xcd, 0xe3, 0x11, 0x8f, 0x76, 0x8f, 0xbc, 0xd0, 0x0c, 0x20, 0xcf, 0x8b, 0x66, 0x49,
- 0x0b, 0x3f, 0xdc, 0xe2, 0x48, 0x5a, 0xf8, 0x91, 0x8a, 0x5b, 0xc2, 0xc2, 0x77, 0x9d, 0x01, 0x56,
- 0xb6, 0x19, 0xaf, 0xa5, 0xa5, 0x49, 0x3b, 0x7a, 0x9b, 0x45, 0x0a, 0x71, 0x69, 0xd2, 0xe4, 0x36,
- 0x13, 0x25, 0x33, 0x94, 0xc2, 0xec, 0x98, 0x6d, 0x16, 0xad, 0xb8, 0x25, 0x6c, 0x33, 0x2a, 0x50,
- 0xd9, 0x66, 0xb2, 0x94, 0x95, 0xb4, 0xcd, 0x62, 0xdd, 0x98, 0xa4, 0x6d, 0x16, 0xaf, 0x86, 0x25,
- 0xf8, 0x91, 0xca, 0x0d, 0x6d, 0xb3, 0xb3, 0x09, 0xc5, 0x2e, 0xf4, 0x6e, 0x8a, 0x11, 0x13, 0x7b,
- 0x3b, 0x8d, 0xdb, 0xaf, 0x89, 0x9d, 0xba, 0xc6, 0x99, 0xf9, 0xc5, 0x1a, 0xff, 0x23, 0x0d, 0xe6,
- 0x92, 0xea, 0x63, 0x28, 0x45, 0x4e, 0x4a, 0x2b, 0xa8, 0xb1, 0xf8, 0xba, 0xe8, 0x47, 0x5b, 0x2b,
- 0x58, 0xf5, 0x0f, 0xfb, 0x5f, 0xb4, 0x9a, 0x2f, 0xae, 0xc2, 0x15, 0x98, 0x69, 0x8d, 0xac, 0x27,
- 0xf8, 0x10, 0x9d, 0x2d, 0x64, 0x1a, 0x15, 0xc2, 0xd7, 0x71, 0xad, 0xcf, 0xe8, 0x5f, 0xbd, 0x58,
- 0xc8, 0xec, 0x96, 0x01, 0x02, 0x84, 0xa9, 0x7f, 0xff, 0x72, 0x5e, 0xfb, 0xaf, 0x2f, 0xe7, 0xb5,
- 0xff, 0xf9, 0x72, 0x5e, 0xfb, 0xe9, 0xff, 0xcd, 0x4f, 0xbd, 0xb8, 0xd6, 0x77, 0xa8, 0x5a, 0x8b,
- 0x96, 0xd3, 0x94, 0x7f, 0x89, 0x63, 0xb9, 0xa9, 0xaa, 0xba, 0x3b, 0x43, 0xff, 0x74, 0xc6, 0xf2,
- 0xcf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x82, 0x9b, 0xab, 0xde, 0x11, 0x44, 0x00, 0x00,
-}
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion4
-
-// KVClient is the client API for KV service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type KVClient interface {
- // Range gets the keys in the range from the key-value store.
- Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error)
- // Put puts the given key into the key-value store.
- // A put request increments the revision of the key-value store
- // and generates one event in the event history.
- Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error)
- // DeleteRange deletes the given range from the key-value store.
- // A delete request increments the revision of the key-value store
- // and generates a delete event in the event history for every deleted key.
- DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error)
- // Txn processes multiple requests in a single transaction.
- // A txn request increments the revision of the key-value store
- // and generates events with the same revision for every completed request.
- // It is not allowed to modify the same key several times within one txn.
- Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error)
- // Compact compacts the event history in the etcd key-value store. The key-value
- // store should be periodically compacted or the event history will continue to grow
- // indefinitely.
- Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error)
-}
-
-type kVClient struct {
- cc *grpc.ClientConn
-}
-
-func NewKVClient(cc *grpc.ClientConn) KVClient {
- return &kVClient{cc}
-}
-
-func (c *kVClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error) {
- out := new(RangeResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.KV/Range", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *kVClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) {
- out := new(PutResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.KV/Put", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *kVClient) DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error) {
- out := new(DeleteRangeResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.KV/DeleteRange", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *kVClient) Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error) {
- out := new(TxnResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.KV/Txn", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *kVClient) Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error) {
- out := new(CompactionResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.KV/Compact", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// KVServer is the server API for KV service.
-type KVServer interface {
- // Range gets the keys in the range from the key-value store.
- Range(context.Context, *RangeRequest) (*RangeResponse, error)
- // Put puts the given key into the key-value store.
- // A put request increments the revision of the key-value store
- // and generates one event in the event history.
- Put(context.Context, *PutRequest) (*PutResponse, error)
- // DeleteRange deletes the given range from the key-value store.
- // A delete request increments the revision of the key-value store
- // and generates a delete event in the event history for every deleted key.
- DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error)
- // Txn processes multiple requests in a single transaction.
- // A txn request increments the revision of the key-value store
- // and generates events with the same revision for every completed request.
- // It is not allowed to modify the same key several times within one txn.
- Txn(context.Context, *TxnRequest) (*TxnResponse, error)
- // Compact compacts the event history in the etcd key-value store. The key-value
- // store should be periodically compacted or the event history will continue to grow
- // indefinitely.
- Compact(context.Context, *CompactionRequest) (*CompactionResponse, error)
-}
-
-// UnimplementedKVServer can be embedded to have forward compatible implementations.
-type UnimplementedKVServer struct {
+type AuthUserListResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ Users []string `protobuf:"bytes,2,rep,name=users,proto3" json:"users,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (*UnimplementedKVServer) Range(ctx context.Context, req *RangeRequest) (*RangeResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Range not implemented")
-}
-func (*UnimplementedKVServer) Put(ctx context.Context, req *PutRequest) (*PutResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Put not implemented")
-}
-func (*UnimplementedKVServer) DeleteRange(ctx context.Context, req *DeleteRangeRequest) (*DeleteRangeResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method DeleteRange not implemented")
-}
-func (*UnimplementedKVServer) Txn(ctx context.Context, req *TxnRequest) (*TxnResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Txn not implemented")
-}
-func (*UnimplementedKVServer) Compact(ctx context.Context, req *CompactionRequest) (*CompactionResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Compact not implemented")
+func (x *AuthUserListResponse) Reset() {
+ *x = AuthUserListResponse{}
+ mi := &file_rpc_proto_msgTypes[93]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func RegisterKVServer(s *grpc.Server, srv KVServer) {
- s.RegisterService(&_KV_serviceDesc, srv)
+func (x *AuthUserListResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func _KV_Range_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(RangeRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(KVServer).Range(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.KV/Range",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(KVServer).Range(ctx, req.(*RangeRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
+func (*AuthUserListResponse) ProtoMessage() {}
-func _KV_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(PutRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(KVServer).Put(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.KV/Put",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(KVServer).Put(ctx, req.(*PutRequest))
+func (x *AuthUserListResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[93]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return interceptor(ctx, in, info, handler)
+ return mi.MessageOf(x)
}
-func _KV_DeleteRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(DeleteRangeRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(KVServer).DeleteRange(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.KV/DeleteRange",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(KVServer).DeleteRange(ctx, req.(*DeleteRangeRequest))
- }
- return interceptor(ctx, in, info, handler)
+// Deprecated: Use AuthUserListResponse.ProtoReflect.Descriptor instead.
+func (*AuthUserListResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{93}
}
-func _KV_Txn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(TxnRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(KVServer).Txn(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.KV/Txn",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(KVServer).Txn(ctx, req.(*TxnRequest))
+func (x *AuthUserListResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
- return interceptor(ctx, in, info, handler)
+ return nil
}
-func _KV_Compact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(CompactionRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(KVServer).Compact(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.KV/Compact",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(KVServer).Compact(ctx, req.(*CompactionRequest))
+func (x *AuthUserListResponse) GetUsers() []string {
+ if x != nil {
+ return x.Users
}
- return interceptor(ctx, in, info, handler)
-}
-
-var _KV_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.KV",
- HandlerType: (*KVServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "Range",
- Handler: _KV_Range_Handler,
- },
- {
- MethodName: "Put",
- Handler: _KV_Put_Handler,
- },
- {
- MethodName: "DeleteRange",
- Handler: _KV_DeleteRange_Handler,
- },
- {
- MethodName: "Txn",
- Handler: _KV_Txn_Handler,
- },
- {
- MethodName: "Compact",
- Handler: _KV_Compact_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "rpc.proto",
-}
-
-// WatchClient is the client API for Watch service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type WatchClient interface {
- // Watch watches for events happening or that have happened. Both input and output
- // are streams; the input stream is for creating and canceling watchers and the output
- // stream sends events. One watch RPC can watch on multiple key ranges, streaming events
- // for several watches at once. The entire event history can be watched starting from the
- // last compaction revision.
- Watch(ctx context.Context, opts ...grpc.CallOption) (Watch_WatchClient, error)
-}
-
-type watchClient struct {
- cc *grpc.ClientConn
-}
-
-func NewWatchClient(cc *grpc.ClientConn) WatchClient {
- return &watchClient{cc}
+ return nil
}
-func (c *watchClient) Watch(ctx context.Context, opts ...grpc.CallOption) (Watch_WatchClient, error) {
- stream, err := c.cc.NewStream(ctx, &_Watch_serviceDesc.Streams[0], "/etcdserverpb.Watch/Watch", opts...)
- if err != nil {
- return nil, err
- }
- x := &watchWatchClient{stream}
- return x, nil
+type AuthRoleDeleteResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-type Watch_WatchClient interface {
- Send(*WatchRequest) error
- Recv() (*WatchResponse, error)
- grpc.ClientStream
+func (x *AuthRoleDeleteResponse) Reset() {
+ *x = AuthRoleDeleteResponse{}
+ mi := &file_rpc_proto_msgTypes[94]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-type watchWatchClient struct {
- grpc.ClientStream
+func (x *AuthRoleDeleteResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (x *watchWatchClient) Send(m *WatchRequest) error {
- return x.ClientStream.SendMsg(m)
-}
+func (*AuthRoleDeleteResponse) ProtoMessage() {}
-func (x *watchWatchClient) Recv() (*WatchResponse, error) {
- m := new(WatchResponse)
- if err := x.ClientStream.RecvMsg(m); err != nil {
- return nil, err
+func (x *AuthRoleDeleteResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[94]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return m, nil
-}
-
-// WatchServer is the server API for Watch service.
-type WatchServer interface {
- // Watch watches for events happening or that have happened. Both input and output
- // are streams; the input stream is for creating and canceling watchers and the output
- // stream sends events. One watch RPC can watch on multiple key ranges, streaming events
- // for several watches at once. The entire event history can be watched starting from the
- // last compaction revision.
- Watch(Watch_WatchServer) error
-}
-
-// UnimplementedWatchServer can be embedded to have forward compatible implementations.
-type UnimplementedWatchServer struct {
-}
-
-func (*UnimplementedWatchServer) Watch(srv Watch_WatchServer) error {
- return status.Errorf(codes.Unimplemented, "method Watch not implemented")
-}
-
-func RegisterWatchServer(s *grpc.Server, srv WatchServer) {
- s.RegisterService(&_Watch_serviceDesc, srv)
-}
-
-func _Watch_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
- return srv.(WatchServer).Watch(&watchWatchServer{stream})
-}
-
-type Watch_WatchServer interface {
- Send(*WatchResponse) error
- Recv() (*WatchRequest, error)
- grpc.ServerStream
-}
-
-type watchWatchServer struct {
- grpc.ServerStream
+ return mi.MessageOf(x)
}
-func (x *watchWatchServer) Send(m *WatchResponse) error {
- return x.ServerStream.SendMsg(m)
+// Deprecated: Use AuthRoleDeleteResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleDeleteResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{94}
}
-func (x *watchWatchServer) Recv() (*WatchRequest, error) {
- m := new(WatchRequest)
- if err := x.ServerStream.RecvMsg(m); err != nil {
- return nil, err
+func (x *AuthRoleDeleteResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
- return m, nil
+ return nil
}
-var _Watch_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.Watch",
- HandlerType: (*WatchServer)(nil),
- Methods: []grpc.MethodDesc{},
- Streams: []grpc.StreamDesc{
- {
- StreamName: "Watch",
- Handler: _Watch_Watch_Handler,
- ServerStreams: true,
- ClientStreams: true,
- },
- },
- Metadata: "rpc.proto",
+type AuthRoleGrantPermissionResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-// LeaseClient is the client API for Lease service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type LeaseClient interface {
- // LeaseGrant creates a lease which expires if the server does not receive a keepAlive
- // within a given time to live period. All keys attached to the lease will be expired and
- // deleted if the lease expires. Each expired key generates a delete event in the event history.
- LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error)
- // LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
- LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error)
- // LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
- // to the server and streaming keep alive responses from the server to the client.
- LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (Lease_LeaseKeepAliveClient, error)
- // LeaseTimeToLive retrieves lease information.
- LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error)
- // LeaseLeases lists all existing leases.
- LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error)
+func (x *AuthRoleGrantPermissionResponse) Reset() {
+ *x = AuthRoleGrantPermissionResponse{}
+ mi := &file_rpc_proto_msgTypes[95]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-type leaseClient struct {
- cc *grpc.ClientConn
+func (x *AuthRoleGrantPermissionResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func NewLeaseClient(cc *grpc.ClientConn) LeaseClient {
- return &leaseClient{cc}
-}
+func (*AuthRoleGrantPermissionResponse) ProtoMessage() {}
-func (c *leaseClient) LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error) {
- out := new(LeaseGrantResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Lease/LeaseGrant", in, out, opts...)
- if err != nil {
- return nil, err
+func (x *AuthRoleGrantPermissionResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[95]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return out, nil
+ return mi.MessageOf(x)
}
-func (c *leaseClient) LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error) {
- out := new(LeaseRevokeResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Lease/LeaseRevoke", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
+// Deprecated: Use AuthRoleGrantPermissionResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleGrantPermissionResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{95}
}
-func (c *leaseClient) LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (Lease_LeaseKeepAliveClient, error) {
- stream, err := c.cc.NewStream(ctx, &_Lease_serviceDesc.Streams[0], "/etcdserverpb.Lease/LeaseKeepAlive", opts...)
- if err != nil {
- return nil, err
+func (x *AuthRoleGrantPermissionResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
- x := &leaseLeaseKeepAliveClient{stream}
- return x, nil
-}
-
-type Lease_LeaseKeepAliveClient interface {
- Send(*LeaseKeepAliveRequest) error
- Recv() (*LeaseKeepAliveResponse, error)
- grpc.ClientStream
+ return nil
}
-type leaseLeaseKeepAliveClient struct {
- grpc.ClientStream
+type AuthRoleRevokePermissionResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (x *leaseLeaseKeepAliveClient) Send(m *LeaseKeepAliveRequest) error {
- return x.ClientStream.SendMsg(m)
+func (x *AuthRoleRevokePermissionResponse) Reset() {
+ *x = AuthRoleRevokePermissionResponse{}
+ mi := &file_rpc_proto_msgTypes[96]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (x *leaseLeaseKeepAliveClient) Recv() (*LeaseKeepAliveResponse, error) {
- m := new(LeaseKeepAliveResponse)
- if err := x.ClientStream.RecvMsg(m); err != nil {
- return nil, err
- }
- return m, nil
+func (x *AuthRoleRevokePermissionResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (c *leaseClient) LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error) {
- out := new(LeaseTimeToLiveResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Lease/LeaseTimeToLive", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
+func (*AuthRoleRevokePermissionResponse) ProtoMessage() {}
-func (c *leaseClient) LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error) {
- out := new(LeaseLeasesResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Lease/LeaseLeases", in, out, opts...)
- if err != nil {
- return nil, err
+func (x *AuthRoleRevokePermissionResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[96]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return out, nil
+ return mi.MessageOf(x)
}
-// LeaseServer is the server API for Lease service.
-type LeaseServer interface {
- // LeaseGrant creates a lease which expires if the server does not receive a keepAlive
- // within a given time to live period. All keys attached to the lease will be expired and
- // deleted if the lease expires. Each expired key generates a delete event in the event history.
- LeaseGrant(context.Context, *LeaseGrantRequest) (*LeaseGrantResponse, error)
- // LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
- LeaseRevoke(context.Context, *LeaseRevokeRequest) (*LeaseRevokeResponse, error)
- // LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
- // to the server and streaming keep alive responses from the server to the client.
- LeaseKeepAlive(Lease_LeaseKeepAliveServer) error
- // LeaseTimeToLive retrieves lease information.
- LeaseTimeToLive(context.Context, *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error)
- // LeaseLeases lists all existing leases.
- LeaseLeases(context.Context, *LeaseLeasesRequest) (*LeaseLeasesResponse, error)
-}
-
-// UnimplementedLeaseServer can be embedded to have forward compatible implementations.
-type UnimplementedLeaseServer struct {
-}
-
-func (*UnimplementedLeaseServer) LeaseGrant(ctx context.Context, req *LeaseGrantRequest) (*LeaseGrantResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method LeaseGrant not implemented")
-}
-func (*UnimplementedLeaseServer) LeaseRevoke(ctx context.Context, req *LeaseRevokeRequest) (*LeaseRevokeResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method LeaseRevoke not implemented")
-}
-func (*UnimplementedLeaseServer) LeaseKeepAlive(srv Lease_LeaseKeepAliveServer) error {
- return status.Errorf(codes.Unimplemented, "method LeaseKeepAlive not implemented")
-}
-func (*UnimplementedLeaseServer) LeaseTimeToLive(ctx context.Context, req *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method LeaseTimeToLive not implemented")
-}
-func (*UnimplementedLeaseServer) LeaseLeases(ctx context.Context, req *LeaseLeasesRequest) (*LeaseLeasesResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method LeaseLeases not implemented")
-}
-
-func RegisterLeaseServer(s *grpc.Server, srv LeaseServer) {
- s.RegisterService(&_Lease_serviceDesc, srv)
-}
-
-func _Lease_LeaseGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(LeaseGrantRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(LeaseServer).LeaseGrant(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Lease/LeaseGrant",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LeaseServer).LeaseGrant(ctx, req.(*LeaseGrantRequest))
- }
- return interceptor(ctx, in, info, handler)
+// Deprecated: Use AuthRoleRevokePermissionResponse.ProtoReflect.Descriptor instead.
+func (*AuthRoleRevokePermissionResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{96}
}
-func _Lease_LeaseRevoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(LeaseRevokeRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(LeaseServer).LeaseRevoke(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Lease/LeaseRevoke",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LeaseServer).LeaseRevoke(ctx, req.(*LeaseRevokeRequest))
+func (x *AuthRoleRevokePermissionResponse) GetHeader() *ResponseHeader {
+ if x != nil {
+ return x.Header
}
- return interceptor(ctx, in, info, handler)
-}
-
-func _Lease_LeaseKeepAlive_Handler(srv interface{}, stream grpc.ServerStream) error {
- return srv.(LeaseServer).LeaseKeepAlive(&leaseLeaseKeepAliveServer{stream})
-}
-
-type Lease_LeaseKeepAliveServer interface {
- Send(*LeaseKeepAliveResponse) error
- Recv() (*LeaseKeepAliveRequest, error)
- grpc.ServerStream
-}
-
-type leaseLeaseKeepAliveServer struct {
- grpc.ServerStream
-}
-
-func (x *leaseLeaseKeepAliveServer) Send(m *LeaseKeepAliveResponse) error {
- return x.ServerStream.SendMsg(m)
+ return nil
}
-func (x *leaseLeaseKeepAliveServer) Recv() (*LeaseKeepAliveRequest, error) {
- m := new(LeaseKeepAliveRequest)
- if err := x.ServerStream.RecvMsg(m); err != nil {
- return nil, err
- }
- return m, nil
-}
+// RangeStreamResponse is the response for the RangeStream RPC.
+type RangeStreamResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // range_response is a partial response for the KV.RangeStream RPC.
+ // The result of proto.Merge() applied on all responses should result in the
+ // same RangeResponse response as to the Range() method.
+ //
+ // Field population across chunks:
+ // - kvs: each chunk carries a disjoint slice of the result. Concatenating
+ // them in order yields the full key set.
+ // - header, more, count: set only on the final chunk. Non-merge clients
+ // can rely on io.EOF (end of stream) to detect completion. These
+ // fields are provided for clients that merge all responses into a
+ // single RangeResponse.
+ RangeResponse *RangeResponse `protobuf:"bytes,1,opt,name=range_response,json=rangeResponse,proto3" json:"range_response,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *RangeStreamResponse) Reset() {
+ *x = RangeStreamResponse{}
+ mi := &file_rpc_proto_msgTypes[97]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *RangeStreamResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RangeStreamResponse) ProtoMessage() {}
+
+func (x *RangeStreamResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_rpc_proto_msgTypes[97]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use RangeStreamResponse.ProtoReflect.Descriptor instead.
+func (*RangeStreamResponse) Descriptor() ([]byte, []int) {
+ return file_rpc_proto_rawDescGZIP(), []int{97}
+}
+
+func (x *RangeStreamResponse) GetRangeResponse() *RangeResponse {
+ if x != nil {
+ return x.RangeResponse
+ }
+ return nil
+}
+
+var File_rpc_proto protoreflect.FileDescriptor
+
+const file_rpc_proto_rawDesc = "" +
+ "\n" +
+ "\trpc.proto\x12\fetcdserverpb\x1a\x18etcd/api/mvccpb/kv.proto\x1a\x1aetcd/api/authpb/auth.proto\x1a etcd/api/versionpb/version.proto\x1a\x1cgoogle/api/annotations.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\"\x8e\x01\n" +
+ "\x0eResponseHeader\x12\x1d\n" +
+ "\n" +
+ "cluster_id\x18\x01 \x01(\x04R\tclusterId\x12\x1b\n" +
+ "\tmember_id\x18\x02 \x01(\x04R\bmemberId\x12\x1a\n" +
+ "\brevision\x18\x03 \x01(\x03R\brevision\x12\x1b\n" +
+ "\traft_term\x18\x04 \x01(\x04R\braftTerm:\a\x82\xb5\x18\x033.0\"\xc3\x05\n" +
+ "\fRangeRequest\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\x12\x1b\n" +
+ "\trange_end\x18\x02 \x01(\fR\brangeEnd\x12\x14\n" +
+ "\x05limit\x18\x03 \x01(\x03R\x05limit\x12\x1a\n" +
+ "\brevision\x18\x04 \x01(\x03R\brevision\x12C\n" +
+ "\n" +
+ "sort_order\x18\x05 \x01(\x0e2$.etcdserverpb.RangeRequest.SortOrderR\tsortOrder\x12F\n" +
+ "\vsort_target\x18\x06 \x01(\x0e2%.etcdserverpb.RangeRequest.SortTargetR\n" +
+ "sortTarget\x12\"\n" +
+ "\fserializable\x18\a \x01(\bR\fserializable\x12\x1b\n" +
+ "\tkeys_only\x18\b \x01(\bR\bkeysOnly\x12\x1d\n" +
+ "\n" +
+ "count_only\x18\t \x01(\bR\tcountOnly\x121\n" +
+ "\x10min_mod_revision\x18\n" +
+ " \x01(\x03B\a\x8a\xb5\x18\x033.1R\x0eminModRevision\x121\n" +
+ "\x10max_mod_revision\x18\v \x01(\x03B\a\x8a\xb5\x18\x033.1R\x0emaxModRevision\x127\n" +
+ "\x13min_create_revision\x18\f \x01(\x03B\a\x8a\xb5\x18\x033.1R\x11minCreateRevision\x127\n" +
+ "\x13max_create_revision\x18\r \x01(\x03B\a\x8a\xb5\x18\x033.1R\x11maxCreateRevision\"7\n" +
+ "\tSortOrder\x12\b\n" +
+ "\x04NONE\x10\x00\x12\n" +
+ "\n" +
+ "\x06ASCEND\x10\x01\x12\v\n" +
+ "\aDESCEND\x10\x02\x1a\a\x92\xb5\x18\x033.0\"K\n" +
+ "\n" +
+ "SortTarget\x12\a\n" +
+ "\x03KEY\x10\x00\x12\v\n" +
+ "\aVERSION\x10\x01\x12\n" +
+ "\n" +
+ "\x06CREATE\x10\x02\x12\a\n" +
+ "\x03MOD\x10\x03\x12\t\n" +
+ "\x05VALUE\x10\x04\x1a\a\x92\xb5\x18\x033.0:\a\x82\xb5\x18\x033.0\"\x9c\x01\n" +
+ "\rRangeResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\"\n" +
+ "\x03kvs\x18\x02 \x03(\v2\x10.mvccpb.KeyValueR\x03kvs\x12\x12\n" +
+ "\x04more\x18\x03 \x01(\bR\x04more\x12\x14\n" +
+ "\x05count\x18\x04 \x01(\x03R\x05count:\a\x82\xb5\x18\x033.0\"\xcd\x01\n" +
+ "\n" +
+ "PutRequest\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\x12\x14\n" +
+ "\x05value\x18\x02 \x01(\fR\x05value\x12\x14\n" +
+ "\x05lease\x18\x03 \x01(\x03R\x05lease\x12 \n" +
+ "\aprev_kv\x18\x04 \x01(\bB\a\x8a\xb5\x18\x033.1R\x06prevKv\x12*\n" +
+ "\fignore_value\x18\x05 \x01(\bB\a\x8a\xb5\x18\x033.2R\vignoreValue\x12*\n" +
+ "\fignore_lease\x18\x06 \x01(\bB\a\x8a\xb5\x18\x033.2R\vignoreLease:\a\x82\xb5\x18\x033.0\"\x80\x01\n" +
+ "\vPutResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x122\n" +
+ "\aprev_kv\x18\x02 \x01(\v2\x10.mvccpb.KeyValueB\a\x8a\xb5\x18\x033.1R\x06prevKv:\a\x82\xb5\x18\x033.0\"n\n" +
+ "\x12DeleteRangeRequest\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\x12\x1b\n" +
+ "\trange_end\x18\x02 \x01(\fR\brangeEnd\x12 \n" +
+ "\aprev_kv\x18\x03 \x01(\bB\a\x8a\xb5\x18\x033.1R\x06prevKv:\a\x82\xb5\x18\x033.0\"\xa4\x01\n" +
+ "\x13DeleteRangeResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x18\n" +
+ "\adeleted\x18\x02 \x01(\x03R\adeleted\x124\n" +
+ "\bprev_kvs\x18\x03 \x03(\v2\x10.mvccpb.KeyValueB\a\x8a\xb5\x18\x033.1R\aprevKvs:\a\x82\xb5\x18\x033.0\"\xbb\x02\n" +
+ "\tRequestOp\x12A\n" +
+ "\rrequest_range\x18\x01 \x01(\v2\x1a.etcdserverpb.RangeRequestH\x00R\frequestRange\x12;\n" +
+ "\vrequest_put\x18\x02 \x01(\v2\x18.etcdserverpb.PutRequestH\x00R\n" +
+ "requestPut\x12T\n" +
+ "\x14request_delete_range\x18\x03 \x01(\v2 .etcdserverpb.DeleteRangeRequestH\x00R\x12requestDeleteRange\x12D\n" +
+ "\vrequest_txn\x18\x04 \x01(\v2\x18.etcdserverpb.TxnRequestB\a\x8a\xb5\x18\x033.3H\x00R\n" +
+ "requestTxn:\a\x82\xb5\x18\x033.0B\t\n" +
+ "\arequest\"\xc9\x02\n" +
+ "\n" +
+ "ResponseOp\x12D\n" +
+ "\x0eresponse_range\x18\x01 \x01(\v2\x1b.etcdserverpb.RangeResponseH\x00R\rresponseRange\x12>\n" +
+ "\fresponse_put\x18\x02 \x01(\v2\x19.etcdserverpb.PutResponseH\x00R\vresponsePut\x12W\n" +
+ "\x15response_delete_range\x18\x03 \x01(\v2!.etcdserverpb.DeleteRangeResponseH\x00R\x13responseDeleteRange\x12G\n" +
+ "\fresponse_txn\x18\x04 \x01(\v2\x19.etcdserverpb.TxnResponseB\a\x8a\xb5\x18\x033.3H\x00R\vresponseTxn:\a\x82\xb5\x18\x033.0B\n" +
+ "\n" +
+ "\bresponse\"\xa8\x04\n" +
+ "\aCompare\x12;\n" +
+ "\x06result\x18\x01 \x01(\x0e2#.etcdserverpb.Compare.CompareResultR\x06result\x12;\n" +
+ "\x06target\x18\x02 \x01(\x0e2#.etcdserverpb.Compare.CompareTargetR\x06target\x12\x10\n" +
+ "\x03key\x18\x03 \x01(\fR\x03key\x12\x1a\n" +
+ "\aversion\x18\x04 \x01(\x03H\x00R\aversion\x12)\n" +
+ "\x0fcreate_revision\x18\x05 \x01(\x03H\x00R\x0ecreateRevision\x12#\n" +
+ "\fmod_revision\x18\x06 \x01(\x03H\x00R\vmodRevision\x12\x16\n" +
+ "\x05value\x18\a \x01(\fH\x00R\x05value\x12\x1f\n" +
+ "\x05lease\x18\b \x01(\x03B\a\x8a\xb5\x18\x033.3H\x00R\x05lease\x12$\n" +
+ "\trange_end\x18@ \x01(\fB\a\x8a\xb5\x18\x033.3R\brangeEnd\"R\n" +
+ "\rCompareResult\x12\t\n" +
+ "\x05EQUAL\x10\x00\x12\v\n" +
+ "\aGREATER\x10\x01\x12\b\n" +
+ "\x04LESS\x10\x02\x12\x16\n" +
+ "\tNOT_EQUAL\x10\x03\x1a\a\x9a\xb5\x18\x033.1\x1a\a\x92\xb5\x18\x033.0\"Y\n" +
+ "\rCompareTarget\x12\v\n" +
+ "\aVERSION\x10\x00\x12\n" +
+ "\n" +
+ "\x06CREATE\x10\x01\x12\a\n" +
+ "\x03MOD\x10\x02\x12\t\n" +
+ "\x05VALUE\x10\x03\x12\x12\n" +
+ "\x05LEASE\x10\x04\x1a\a\x9a\xb5\x18\x033.3\x1a\a\x92\xb5\x18\x033.0:\a\x82\xb5\x18\x033.0B\x0e\n" +
+ "\ftarget_union\"\xac\x01\n" +
+ "\n" +
+ "TxnRequest\x12/\n" +
+ "\acompare\x18\x01 \x03(\v2\x15.etcdserverpb.CompareR\acompare\x121\n" +
+ "\asuccess\x18\x02 \x03(\v2\x17.etcdserverpb.RequestOpR\asuccess\x121\n" +
+ "\afailure\x18\x03 \x03(\v2\x17.etcdserverpb.RequestOpR\afailure:\a\x82\xb5\x18\x033.0\"\xa2\x01\n" +
+ "\vTxnResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x1c\n" +
+ "\tsucceeded\x18\x02 \x01(\bR\tsucceeded\x126\n" +
+ "\tresponses\x18\x03 \x03(\v2\x18.etcdserverpb.ResponseOpR\tresponses:\a\x82\xb5\x18\x033.0\"T\n" +
+ "\x11CompactionRequest\x12\x1a\n" +
+ "\brevision\x18\x01 \x01(\x03R\brevision\x12\x1a\n" +
+ "\bphysical\x18\x02 \x01(\bR\bphysical:\a\x82\xb5\x18\x033.0\"S\n" +
+ "\x12CompactionResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"\x16\n" +
+ "\vHashRequest:\a\x82\xb5\x18\x033.0\"4\n" +
+ "\rHashKVRequest\x12\x1a\n" +
+ "\brevision\x18\x01 \x01(\x03R\brevision:\a\x82\xb5\x18\x033.3\"\xbc\x01\n" +
+ "\x0eHashKVResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x12\n" +
+ "\x04hash\x18\x02 \x01(\rR\x04hash\x12)\n" +
+ "\x10compact_revision\x18\x03 \x01(\x03R\x0fcompactRevision\x12,\n" +
+ "\rhash_revision\x18\x04 \x01(\x03B\a\x8a\xb5\x18\x033.6R\fhashRevision:\a\x82\xb5\x18\x033.3\"a\n" +
+ "\fHashResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x12\n" +
+ "\x04hash\x18\x02 \x01(\rR\x04hash:\a\x82\xb5\x18\x033.0\"\x1a\n" +
+ "\x0fSnapshotRequest:\a\x82\xb5\x18\x033.3\"\xb1\x01\n" +
+ "\x10SnapshotResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12'\n" +
+ "\x0fremaining_bytes\x18\x02 \x01(\x04R\x0eremainingBytes\x12\x12\n" +
+ "\x04blob\x18\x03 \x01(\fR\x04blob\x12!\n" +
+ "\aversion\x18\x04 \x01(\tB\a\x8a\xb5\x18\x033.6R\aversion:\a\x82\xb5\x18\x033.3\"\x98\x02\n" +
+ "\fWatchRequest\x12I\n" +
+ "\x0ecreate_request\x18\x01 \x01(\v2 .etcdserverpb.WatchCreateRequestH\x00R\rcreateRequest\x12I\n" +
+ "\x0ecancel_request\x18\x02 \x01(\v2 .etcdserverpb.WatchCancelRequestH\x00R\rcancelRequest\x12X\n" +
+ "\x10progress_request\x18\x03 \x01(\v2\".etcdserverpb.WatchProgressRequestB\a\x8a\xb5\x18\x033.4H\x00R\x0fprogressRequest:\a\x82\xb5\x18\x033.0B\x0f\n" +
+ "\rrequest_union\"\x87\x03\n" +
+ "\x12WatchCreateRequest\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\x12\x1b\n" +
+ "\trange_end\x18\x02 \x01(\fR\brangeEnd\x12%\n" +
+ "\x0estart_revision\x18\x03 \x01(\x03R\rstartRevision\x12'\n" +
+ "\x0fprogress_notify\x18\x04 \x01(\bR\x0eprogressNotify\x12N\n" +
+ "\afilters\x18\x05 \x03(\x0e2+.etcdserverpb.WatchCreateRequest.FilterTypeB\a\x8a\xb5\x18\x033.1R\afilters\x12 \n" +
+ "\aprev_kv\x18\x06 \x01(\bB\a\x8a\xb5\x18\x033.1R\x06prevKv\x12\"\n" +
+ "\bwatch_id\x18\a \x01(\x03B\a\x8a\xb5\x18\x033.4R\awatchId\x12#\n" +
+ "\bfragment\x18\b \x01(\bB\a\x8a\xb5\x18\x033.4R\bfragment\".\n" +
+ "\n" +
+ "FilterType\x12\t\n" +
+ "\x05NOPUT\x10\x00\x12\f\n" +
+ "\bNODELETE\x10\x01\x1a\a\x92\xb5\x18\x033.1:\a\x82\xb5\x18\x033.0\"A\n" +
+ "\x12WatchCancelRequest\x12\"\n" +
+ "\bwatch_id\x18\x01 \x01(\x03B\a\x8a\xb5\x18\x033.1R\awatchId:\a\x82\xb5\x18\x033.1\"\x1f\n" +
+ "\x14WatchProgressRequest:\a\x82\xb5\x18\x033.4\"\xc4\x02\n" +
+ "\rWatchResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x19\n" +
+ "\bwatch_id\x18\x02 \x01(\x03R\awatchId\x12\x18\n" +
+ "\acreated\x18\x03 \x01(\bR\acreated\x12\x1a\n" +
+ "\bcanceled\x18\x04 \x01(\bR\bcanceled\x12)\n" +
+ "\x10compact_revision\x18\x05 \x01(\x03R\x0fcompactRevision\x12,\n" +
+ "\rcancel_reason\x18\x06 \x01(\tB\a\x8a\xb5\x18\x033.4R\fcancelReason\x12#\n" +
+ "\bfragment\x18\a \x01(\bB\a\x8a\xb5\x18\x033.4R\bfragment\x12%\n" +
+ "\x06events\x18\v \x03(\v2\r.mvccpb.EventR\x06events:\a\x82\xb5\x18\x033.0\">\n" +
+ "\x11LeaseGrantRequest\x12\x10\n" +
+ "\x03TTL\x18\x01 \x01(\x03R\x03TTL\x12\x0e\n" +
+ "\x02ID\x18\x02 \x01(\x03R\x02ID:\a\x82\xb5\x18\x033.0\"\x8b\x01\n" +
+ "\x12LeaseGrantResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x0e\n" +
+ "\x02ID\x18\x02 \x01(\x03R\x02ID\x12\x10\n" +
+ "\x03TTL\x18\x03 \x01(\x03R\x03TTL\x12\x14\n" +
+ "\x05error\x18\x04 \x01(\tR\x05error:\a\x82\xb5\x18\x033.0\"-\n" +
+ "\x12LeaseRevokeRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID:\a\x82\xb5\x18\x033.0\"T\n" +
+ "\x13LeaseRevokeResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"O\n" +
+ "\x0fLeaseCheckpoint\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID\x12#\n" +
+ "\rremaining_TTL\x18\x02 \x01(\x03R\fremainingTTL:\a\x82\xb5\x18\x033.4\"b\n" +
+ "\x16LeaseCheckpointRequest\x12?\n" +
+ "\vcheckpoints\x18\x01 \x03(\v2\x1d.etcdserverpb.LeaseCheckpointR\vcheckpoints:\a\x82\xb5\x18\x033.4\"X\n" +
+ "\x17LeaseCheckpointResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.4\"0\n" +
+ "\x15LeaseKeepAliveRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID:\a\x82\xb5\x18\x033.0\"y\n" +
+ "\x16LeaseKeepAliveResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x0e\n" +
+ "\x02ID\x18\x02 \x01(\x03R\x02ID\x12\x10\n" +
+ "\x03TTL\x18\x03 \x01(\x03R\x03TTL:\a\x82\xb5\x18\x033.0\"E\n" +
+ "\x16LeaseTimeToLiveRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID\x12\x12\n" +
+ "\x04keys\x18\x02 \x01(\bR\x04keys:\a\x82\xb5\x18\x033.1\"\xae\x01\n" +
+ "\x17LeaseTimeToLiveResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x0e\n" +
+ "\x02ID\x18\x02 \x01(\x03R\x02ID\x12\x10\n" +
+ "\x03TTL\x18\x03 \x01(\x03R\x03TTL\x12\x1e\n" +
+ "\n" +
+ "grantedTTL\x18\x04 \x01(\x03R\n" +
+ "grantedTTL\x12\x12\n" +
+ "\x04keys\x18\x05 \x03(\fR\x04keys:\a\x82\xb5\x18\x033.1\"\x1d\n" +
+ "\x12LeaseLeasesRequest:\a\x82\xb5\x18\x033.3\"&\n" +
+ "\vLeaseStatus\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID:\a\x82\xb5\x18\x033.3\"\x87\x01\n" +
+ "\x13LeaseLeasesResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x121\n" +
+ "\x06leases\x18\x02 \x03(\v2\x19.etcdserverpb.LeaseStatusR\x06leases:\a\x82\xb5\x18\x033.3\"\x98\x01\n" +
+ "\x06Member\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID\x12\x12\n" +
+ "\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n" +
+ "\bpeerURLs\x18\x03 \x03(\tR\bpeerURLs\x12\x1e\n" +
+ "\n" +
+ "clientURLs\x18\x04 \x03(\tR\n" +
+ "clientURLs\x12%\n" +
+ "\tisLearner\x18\x05 \x01(\bB\a\x8a\xb5\x18\x033.4R\tisLearner:\a\x82\xb5\x18\x033.0\"^\n" +
+ "\x10MemberAddRequest\x12\x1a\n" +
+ "\bpeerURLs\x18\x01 \x03(\tR\bpeerURLs\x12%\n" +
+ "\tisLearner\x18\x02 \x01(\bB\a\x8a\xb5\x18\x033.4R\tisLearner:\a\x82\xb5\x18\x033.0\"\xb0\x01\n" +
+ "\x11MemberAddResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12,\n" +
+ "\x06member\x18\x02 \x01(\v2\x14.etcdserverpb.MemberR\x06member\x12.\n" +
+ "\amembers\x18\x03 \x03(\v2\x14.etcdserverpb.MemberR\amembers:\a\x82\xb5\x18\x033.0\".\n" +
+ "\x13MemberRemoveRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID:\a\x82\xb5\x18\x033.0\"\x85\x01\n" +
+ "\x14MemberRemoveResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12.\n" +
+ "\amembers\x18\x02 \x03(\v2\x14.etcdserverpb.MemberR\amembers:\a\x82\xb5\x18\x033.0\"J\n" +
+ "\x13MemberUpdateRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID\x12\x1a\n" +
+ "\bpeerURLs\x18\x02 \x03(\tR\bpeerURLs:\a\x82\xb5\x18\x033.0\"\x8e\x01\n" +
+ "\x14MemberUpdateResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x127\n" +
+ "\amembers\x18\x02 \x03(\v2\x14.etcdserverpb.MemberB\a\x8a\xb5\x18\x033.1R\amembers:\a\x82\xb5\x18\x033.0\"I\n" +
+ "\x11MemberListRequest\x12+\n" +
+ "\flinearizable\x18\x01 \x01(\bB\a\x8a\xb5\x18\x033.5R\flinearizable:\a\x82\xb5\x18\x033.0\"\x83\x01\n" +
+ "\x12MemberListResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12.\n" +
+ "\amembers\x18\x02 \x03(\v2\x14.etcdserverpb.MemberR\amembers:\a\x82\xb5\x18\x033.0\"/\n" +
+ "\x14MemberPromoteRequest\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID:\a\x82\xb5\x18\x033.4\"\x86\x01\n" +
+ "\x15MemberPromoteResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12.\n" +
+ "\amembers\x18\x02 \x03(\v2\x14.etcdserverpb.MemberR\amembers:\a\x82\xb5\x18\x033.4\"\x1c\n" +
+ "\x11DefragmentRequest:\a\x82\xb5\x18\x033.0\"S\n" +
+ "\x12DefragmentResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"8\n" +
+ "\x11MoveLeaderRequest\x12\x1a\n" +
+ "\btargetID\x18\x01 \x01(\x04R\btargetID:\a\x82\xb5\x18\x033.3\"S\n" +
+ "\x12MoveLeaderResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.3\"\xe1\x01\n" +
+ "\fAlarmRequest\x12>\n" +
+ "\x06action\x18\x01 \x01(\x0e2&.etcdserverpb.AlarmRequest.AlarmActionR\x06action\x12\x1a\n" +
+ "\bmemberID\x18\x02 \x01(\x04R\bmemberID\x12-\n" +
+ "\x05alarm\x18\x03 \x01(\x0e2\x17.etcdserverpb.AlarmTypeR\x05alarm\"=\n" +
+ "\vAlarmAction\x12\a\n" +
+ "\x03GET\x10\x00\x12\f\n" +
+ "\bACTIVATE\x10\x01\x12\x0e\n" +
+ "\n" +
+ "DEACTIVATE\x10\x02\x1a\a\x92\xb5\x18\x033.0:\a\x82\xb5\x18\x033.0\"a\n" +
+ "\vAlarmMember\x12\x1a\n" +
+ "\bmemberID\x18\x01 \x01(\x04R\bmemberID\x12-\n" +
+ "\x05alarm\x18\x02 \x01(\x0e2\x17.etcdserverpb.AlarmTypeR\x05alarm:\a\x82\xb5\x18\x033.0\"\x81\x01\n" +
+ "\rAlarmResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x121\n" +
+ "\x06alarms\x18\x02 \x03(\v2\x19.etcdserverpb.AlarmMemberR\x06alarms:\a\x82\xb5\x18\x033.0\"\xbf\x01\n" +
+ "\x10DowngradeRequest\x12F\n" +
+ "\x06action\x18\x01 \x01(\x0e2..etcdserverpb.DowngradeRequest.DowngradeActionR\x06action\x12\x18\n" +
+ "\aversion\x18\x02 \x01(\tR\aversion\"@\n" +
+ "\x0fDowngradeAction\x12\f\n" +
+ "\bVALIDATE\x10\x00\x12\n" +
+ "\n" +
+ "\x06ENABLE\x10\x01\x12\n" +
+ "\n" +
+ "\x06CANCEL\x10\x02\x1a\a\x92\xb5\x18\x033.5:\a\x82\xb5\x18\x033.5\"l\n" +
+ "\x11DowngradeResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x18\n" +
+ "\aversion\x18\x02 \x01(\tR\aversion:\a\x82\xb5\x18\x033.5\"8\n" +
+ "\x1bDowngradeVersionTestRequest\x12\x10\n" +
+ "\x03ver\x18\x01 \x01(\tR\x03ver:\a\x82\xb5\x18\x033.6\"\x18\n" +
+ "\rStatusRequest:\a\x82\xb5\x18\x033.0\"\xa3\x04\n" +
+ "\x0eStatusResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x18\n" +
+ "\aversion\x18\x02 \x01(\tR\aversion\x12\x16\n" +
+ "\x06dbSize\x18\x03 \x01(\x03R\x06dbSize\x12\x16\n" +
+ "\x06leader\x18\x04 \x01(\x04R\x06leader\x12\x1c\n" +
+ "\traftIndex\x18\x05 \x01(\x04R\traftIndex\x12\x1a\n" +
+ "\braftTerm\x18\x06 \x01(\x04R\braftTerm\x123\n" +
+ "\x10raftAppliedIndex\x18\a \x01(\x04B\a\x8a\xb5\x18\x033.4R\x10raftAppliedIndex\x12\x1f\n" +
+ "\x06errors\x18\b \x03(\tB\a\x8a\xb5\x18\x033.4R\x06errors\x12)\n" +
+ "\vdbSizeInUse\x18\t \x01(\x03B\a\x8a\xb5\x18\x033.4R\vdbSizeInUse\x12%\n" +
+ "\tisLearner\x18\n" +
+ " \x01(\bB\a\x8a\xb5\x18\x033.4R\tisLearner\x12/\n" +
+ "\x0estorageVersion\x18\v \x01(\tB\a\x8a\xb5\x18\x033.6R\x0estorageVersion\x12)\n" +
+ "\vdbSizeQuota\x18\f \x01(\x03B\a\x8a\xb5\x18\x033.6R\vdbSizeQuota\x12J\n" +
+ "\rdowngradeInfo\x18\r \x01(\v2\x1b.etcdserverpb.DowngradeInfoB\a\x8a\xb5\x18\x033.6R\rdowngradeInfo:\a\x82\xb5\x18\x033.0\"O\n" +
+ "\rDowngradeInfo\x12\x18\n" +
+ "\aenabled\x18\x01 \x01(\bR\aenabled\x12$\n" +
+ "\rtargetVersion\x18\x02 \x01(\tR\rtargetVersion\"\x1c\n" +
+ "\x11AuthEnableRequest:\a\x82\xb5\x18\x033.0\"\x1d\n" +
+ "\x12AuthDisableRequest:\a\x82\xb5\x18\x033.0\"\x1c\n" +
+ "\x11AuthStatusRequest:\a\x82\xb5\x18\x033.5\"N\n" +
+ "\x13AuthenticateRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" +
+ "\bpassword\x18\x02 \x01(\tR\bpassword:\a\x82\xb5\x18\x033.0\"\xb9\x01\n" +
+ "\x12AuthUserAddRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" +
+ "\bpassword\x18\x02 \x01(\tR\bpassword\x129\n" +
+ "\aoptions\x18\x03 \x01(\v2\x16.authpb.UserAddOptionsB\a\x8a\xb5\x18\x033.4R\aoptions\x12/\n" +
+ "\x0ehashedPassword\x18\x04 \x01(\tB\a\x8a\xb5\x18\x033.5R\x0ehashedPassword:\a\x82\xb5\x18\x033.0\"1\n" +
+ "\x12AuthUserGetRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name:\a\x82\xb5\x18\x033.0\"4\n" +
+ "\x15AuthUserDeleteRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name:\a\x82\xb5\x18\x033.0\"\x89\x01\n" +
+ "\x1dAuthUserChangePasswordRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" +
+ "\bpassword\x18\x02 \x01(\tR\bpassword\x12/\n" +
+ "\x0ehashedPassword\x18\x03 \x01(\tB\a\x8a\xb5\x18\x033.5R\x0ehashedPassword:\a\x82\xb5\x18\x033.0\"K\n" +
+ "\x18AuthUserGrantRoleRequest\x12\x12\n" +
+ "\x04user\x18\x01 \x01(\tR\x04user\x12\x12\n" +
+ "\x04role\x18\x02 \x01(\tR\x04role:\a\x82\xb5\x18\x033.0\"L\n" +
+ "\x19AuthUserRevokeRoleRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" +
+ "\x04role\x18\x02 \x01(\tR\x04role:\a\x82\xb5\x18\x033.0\"1\n" +
+ "\x12AuthRoleAddRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name:\a\x82\xb5\x18\x033.0\"1\n" +
+ "\x12AuthRoleGetRequest\x12\x12\n" +
+ "\x04role\x18\x01 \x01(\tR\x04role:\a\x82\xb5\x18\x033.0\"\x1e\n" +
+ "\x13AuthUserListRequest:\a\x82\xb5\x18\x033.0\"\x1e\n" +
+ "\x13AuthRoleListRequest:\a\x82\xb5\x18\x033.0\"4\n" +
+ "\x15AuthRoleDeleteRequest\x12\x12\n" +
+ "\x04role\x18\x01 \x01(\tR\x04role:\a\x82\xb5\x18\x033.0\"e\n" +
+ "\x1eAuthRoleGrantPermissionRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12&\n" +
+ "\x04perm\x18\x02 \x01(\v2\x12.authpb.PermissionR\x04perm:\a\x82\xb5\x18\x033.0\"m\n" +
+ "\x1fAuthRoleRevokePermissionRequest\x12\x12\n" +
+ "\x04role\x18\x01 \x01(\tR\x04role\x12\x10\n" +
+ "\x03key\x18\x02 \x01(\fR\x03key\x12\x1b\n" +
+ "\trange_end\x18\x03 \x01(\fR\brangeEnd:\a\x82\xb5\x18\x033.0\"S\n" +
+ "\x12AuthEnableResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"T\n" +
+ "\x13AuthDisableResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"\x91\x01\n" +
+ "\x12AuthStatusResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x18\n" +
+ "\aenabled\x18\x02 \x01(\bR\aenabled\x12\"\n" +
+ "\fauthRevision\x18\x03 \x01(\x04R\fauthRevision:\a\x82\xb5\x18\x033.5\"k\n" +
+ "\x14AuthenticateResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x14\n" +
+ "\x05token\x18\x02 \x01(\tR\x05token:\a\x82\xb5\x18\x033.0\"T\n" +
+ "\x13AuthUserAddResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"j\n" +
+ "\x13AuthUserGetResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x14\n" +
+ "\x05roles\x18\x02 \x03(\tR\x05roles:\a\x82\xb5\x18\x033.0\"W\n" +
+ "\x16AuthUserDeleteResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"_\n" +
+ "\x1eAuthUserChangePasswordResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"Z\n" +
+ "\x19AuthUserGrantRoleResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"[\n" +
+ "\x1aAuthUserRevokeRoleResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"T\n" +
+ "\x13AuthRoleAddResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"\x85\x01\n" +
+ "\x13AuthRoleGetResponse\x12=\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderB\a\x8a\xb5\x18\x033.0R\x06header\x12/\n" +
+ "\x04perm\x18\x02 \x03(\v2\x12.authpb.PermissionB\a\x8a\xb5\x18\x033.0R\x04perm\"k\n" +
+ "\x14AuthRoleListResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x14\n" +
+ "\x05roles\x18\x02 \x03(\tR\x05roles:\a\x82\xb5\x18\x033.0\"k\n" +
+ "\x14AuthUserListResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x14\n" +
+ "\x05users\x18\x02 \x03(\tR\x05users:\a\x82\xb5\x18\x033.0\"W\n" +
+ "\x16AuthRoleDeleteResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"`\n" +
+ "\x1fAuthRoleGrantPermissionResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"a\n" +
+ " AuthRoleRevokePermissionResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header:\a\x82\xb5\x18\x033.0\"b\n" +
+ "\x13RangeStreamResponse\x12B\n" +
+ "\x0erange_response\x18\x01 \x01(\v2\x1b.etcdserverpb.RangeResponseR\rrangeResponse:\a\x82\xb5\x18\x033.7*A\n" +
+ "\tAlarmType\x12\b\n" +
+ "\x04NONE\x10\x00\x12\v\n" +
+ "\aNOSPACE\x10\x01\x12\x14\n" +
+ "\aCORRUPT\x10\x02\x1a\a\x9a\xb5\x18\x033.3\x1a\a\x92\xb5\x18\x033.02\xb6\x04\n" +
+ "\x02KV\x12Y\n" +
+ "\x05Range\x12\x1a.etcdserverpb.RangeRequest\x1a\x1b.etcdserverpb.RangeResponse\"\x17\x82\xd3\xe4\x93\x02\x11:\x01*\"\f/v3/kv/range\x12P\n" +
+ "\vRangeStream\x12\x1a.etcdserverpb.RangeRequest\x1a!.etcdserverpb.RangeStreamResponse\"\x000\x01\x12Q\n" +
+ "\x03Put\x12\x18.etcdserverpb.PutRequest\x1a\x19.etcdserverpb.PutResponse\"\x15\x82\xd3\xe4\x93\x02\x0f:\x01*\"\n" +
+ "/v3/kv/put\x12q\n" +
+ "\vDeleteRange\x12 .etcdserverpb.DeleteRangeRequest\x1a!.etcdserverpb.DeleteRangeResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/v3/kv/deleterange\x12Q\n" +
+ "\x03Txn\x12\x18.etcdserverpb.TxnRequest\x1a\x19.etcdserverpb.TxnResponse\"\x15\x82\xd3\xe4\x93\x02\x0f:\x01*\"\n" +
+ "/v3/kv/txn\x12j\n" +
+ "\aCompact\x12\x1f.etcdserverpb.CompactionRequest\x1a .etcdserverpb.CompactionResponse\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\"\x11/v3/kv/compaction2c\n" +
+ "\x05Watch\x12Z\n" +
+ "\x05Watch\x12\x1a.etcdserverpb.WatchRequest\x1a\x1b.etcdserverpb.WatchResponse\"\x14\x82\xd3\xe4\x93\x02\x0e:\x01*\"\t/v3/watch(\x010\x012\xad\x05\n" +
+ "\x05Lease\x12k\n" +
+ "\n" +
+ "LeaseGrant\x12\x1f.etcdserverpb.LeaseGrantRequest\x1a .etcdserverpb.LeaseGrantResponse\"\x1a\x82\xd3\xe4\x93\x02\x14:\x01*\"\x0f/v3/lease/grant\x12\x89\x01\n" +
+ "\vLeaseRevoke\x12 .etcdserverpb.LeaseRevokeRequest\x1a!.etcdserverpb.LeaseRevokeResponse\"5\x82\xd3\xe4\x93\x02/:\x01*Z\x18:\x01*\"\x13/v3/kv/lease/revoke\"\x10/v3/lease/revoke\x12\x7f\n" +
+ "\x0eLeaseKeepAlive\x12#.etcdserverpb.LeaseKeepAliveRequest\x1a$.etcdserverpb.LeaseKeepAliveResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/v3/lease/keepalive(\x010\x01\x12\x9d\x01\n" +
+ "\x0fLeaseTimeToLive\x12$.etcdserverpb.LeaseTimeToLiveRequest\x1a%.etcdserverpb.LeaseTimeToLiveResponse\"=\x82\xd3\xe4\x93\x027:\x01*Z\x1c:\x01*\"\x17/v3/kv/lease/timetolive\"\x14/v3/lease/timetolive\x12\x89\x01\n" +
+ "\vLeaseLeases\x12 .etcdserverpb.LeaseLeasesRequest\x1a!.etcdserverpb.LeaseLeasesResponse\"5\x82\xd3\xe4\x93\x02/:\x01*Z\x18:\x01*\"\x13/v3/kv/lease/leases\"\x10/v3/lease/leases2\xea\x04\n" +
+ "\aCluster\x12o\n" +
+ "\tMemberAdd\x12\x1e.etcdserverpb.MemberAddRequest\x1a\x1f.etcdserverpb.MemberAddResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v3/cluster/member/add\x12{\n" +
+ "\fMemberRemove\x12!.etcdserverpb.MemberRemoveRequest\x1a\".etcdserverpb.MemberRemoveResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/v3/cluster/member/remove\x12{\n" +
+ "\fMemberUpdate\x12!.etcdserverpb.MemberUpdateRequest\x1a\".etcdserverpb.MemberUpdateResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/v3/cluster/member/update\x12s\n" +
+ "\n" +
+ "MemberList\x12\x1f.etcdserverpb.MemberListRequest\x1a .etcdserverpb.MemberListResponse\"\"\x82\xd3\xe4\x93\x02\x1c:\x01*\"\x17/v3/cluster/member/list\x12\x7f\n" +
+ "\rMemberPromote\x12\".etcdserverpb.MemberPromoteRequest\x1a#.etcdserverpb.MemberPromoteResponse\"%\x82\xd3\xe4\x93\x02\x1f:\x01*\"\x1a/v3/cluster/member/promote2\x80\a\n" +
+ "\vMaintenance\x12b\n" +
+ "\x05Alarm\x12\x1a.etcdserverpb.AlarmRequest\x1a\x1b.etcdserverpb.AlarmResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/v3/maintenance/alarm\x12f\n" +
+ "\x06Status\x12\x1b.etcdserverpb.StatusRequest\x1a\x1c.etcdserverpb.StatusResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v3/maintenance/status\x12v\n" +
+ "\n" +
+ "Defragment\x12\x1f.etcdserverpb.DefragmentRequest\x1a .etcdserverpb.DefragmentResponse\"%\x82\xd3\xe4\x93\x02\x1f:\x01*\"\x1a/v3/maintenance/defragment\x12^\n" +
+ "\x04Hash\x12\x19.etcdserverpb.HashRequest\x1a\x1a.etcdserverpb.HashResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/maintenance/hash\x12f\n" +
+ "\x06HashKV\x12\x1b.etcdserverpb.HashKVRequest\x1a\x1c.etcdserverpb.HashKVResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v3/maintenance/hashkv\x12p\n" +
+ "\bSnapshot\x12\x1d.etcdserverpb.SnapshotRequest\x1a\x1e.etcdserverpb.SnapshotResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v3/maintenance/snapshot0\x01\x12\x7f\n" +
+ "\n" +
+ "MoveLeader\x12\x1f.etcdserverpb.MoveLeaderRequest\x1a .etcdserverpb.MoveLeaderResponse\".\x82\xd3\xe4\x93\x02(:\x01*\"#/v3/maintenance/transfer-leadership\x12r\n" +
+ "\tDowngrade\x12\x1e.etcdserverpb.DowngradeRequest\x1a\x1f.etcdserverpb.DowngradeResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/v3/maintenance/downgrade2\xa7\x10\n" +
+ "\x04Auth\x12k\n" +
+ "\n" +
+ "AuthEnable\x12\x1f.etcdserverpb.AuthEnableRequest\x1a .etcdserverpb.AuthEnableResponse\"\x1a\x82\xd3\xe4\x93\x02\x14:\x01*\"\x0f/v3/auth/enable\x12o\n" +
+ "\vAuthDisable\x12 .etcdserverpb.AuthDisableRequest\x1a!.etcdserverpb.AuthDisableResponse\"\x1b\x82\xd3\xe4\x93\x02\x15:\x01*\"\x10/v3/auth/disable\x12k\n" +
+ "\n" +
+ "AuthStatus\x12\x1f.etcdserverpb.AuthStatusRequest\x1a .etcdserverpb.AuthStatusResponse\"\x1a\x82\xd3\xe4\x93\x02\x14:\x01*\"\x0f/v3/auth/status\x12w\n" +
+ "\fAuthenticate\x12!.etcdserverpb.AuthenticateRequest\x1a\".etcdserverpb.AuthenticateResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/v3/auth/authenticate\x12l\n" +
+ "\aUserAdd\x12 .etcdserverpb.AuthUserAddRequest\x1a!.etcdserverpb.AuthUserAddResponse\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\"\x11/v3/auth/user/add\x12l\n" +
+ "\aUserGet\x12 .etcdserverpb.AuthUserGetRequest\x1a!.etcdserverpb.AuthUserGetResponse\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\"\x11/v3/auth/user/get\x12p\n" +
+ "\bUserList\x12!.etcdserverpb.AuthUserListRequest\x1a\".etcdserverpb.AuthUserListResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/v3/auth/user/list\x12x\n" +
+ "\n" +
+ "UserDelete\x12#.etcdserverpb.AuthUserDeleteRequest\x1a$.etcdserverpb.AuthUserDeleteResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/auth/user/delete\x12\x92\x01\n" +
+ "\x12UserChangePassword\x12+.etcdserverpb.AuthUserChangePasswordRequest\x1a,.etcdserverpb.AuthUserChangePasswordResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v3/auth/user/changepw\x12\x80\x01\n" +
+ "\rUserGrantRole\x12&.etcdserverpb.AuthUserGrantRoleRequest\x1a'.etcdserverpb.AuthUserGrantRoleResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/v3/auth/user/grant\x12\x84\x01\n" +
+ "\x0eUserRevokeRole\x12'.etcdserverpb.AuthUserRevokeRoleRequest\x1a(.etcdserverpb.AuthUserRevokeRoleResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/auth/user/revoke\x12l\n" +
+ "\aRoleAdd\x12 .etcdserverpb.AuthRoleAddRequest\x1a!.etcdserverpb.AuthRoleAddResponse\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\"\x11/v3/auth/role/add\x12l\n" +
+ "\aRoleGet\x12 .etcdserverpb.AuthRoleGetRequest\x1a!.etcdserverpb.AuthRoleGetResponse\"\x1c\x82\xd3\xe4\x93\x02\x16:\x01*\"\x11/v3/auth/role/get\x12p\n" +
+ "\bRoleList\x12!.etcdserverpb.AuthRoleListRequest\x1a\".etcdserverpb.AuthRoleListResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/v3/auth/role/list\x12x\n" +
+ "\n" +
+ "RoleDelete\x12#.etcdserverpb.AuthRoleDeleteRequest\x1a$.etcdserverpb.AuthRoleDeleteResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/auth/role/delete\x12\x92\x01\n" +
+ "\x13RoleGrantPermission\x12,.etcdserverpb.AuthRoleGrantPermissionRequest\x1a-.etcdserverpb.AuthRoleGrantPermissionResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/v3/auth/role/grant\x12\x96\x01\n" +
+ "\x14RoleRevokePermission\x12-.etcdserverpb.AuthRoleRevokePermissionRequest\x1a..etcdserverpb.AuthRoleRevokePermissionResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/auth/role/revokeBW\x92A/Z\x1f\n" +
+ "\x1d\n" +
+ "\x06ApiKey\x12\x13\b\x02\x1a\rAuthorization \x02b\f\n" +
+ "\n" +
+ "\n" +
+ "\x06ApiKey\x12\x00Z#go.etcd.io/etcd/api/v3/etcdserverpbb\x06proto3"
-func _Lease_LeaseTimeToLive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(LeaseTimeToLiveRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(LeaseServer).LeaseTimeToLive(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Lease/LeaseTimeToLive",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LeaseServer).LeaseTimeToLive(ctx, req.(*LeaseTimeToLiveRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
+var (
+ file_rpc_proto_rawDescOnce sync.Once
+ file_rpc_proto_rawDescData []byte
+)
-func _Lease_LeaseLeases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(LeaseLeasesRequest)
- if err := dec(in); err != nil {
- return nil, err
+func file_rpc_proto_rawDescGZIP() []byte {
+ file_rpc_proto_rawDescOnce.Do(func() {
+ file_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rpc_proto_rawDesc), len(file_rpc_proto_rawDesc)))
+ })
+ return file_rpc_proto_rawDescData
+}
+
+var file_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 8)
+var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 98)
+var file_rpc_proto_goTypes = []any{
+ (AlarmType)(0), // 0: etcdserverpb.AlarmType
+ (RangeRequest_SortOrder)(0), // 1: etcdserverpb.RangeRequest.SortOrder
+ (RangeRequest_SortTarget)(0), // 2: etcdserverpb.RangeRequest.SortTarget
+ (Compare_CompareResult)(0), // 3: etcdserverpb.Compare.CompareResult
+ (Compare_CompareTarget)(0), // 4: etcdserverpb.Compare.CompareTarget
+ (WatchCreateRequest_FilterType)(0), // 5: etcdserverpb.WatchCreateRequest.FilterType
+ (AlarmRequest_AlarmAction)(0), // 6: etcdserverpb.AlarmRequest.AlarmAction
+ (DowngradeRequest_DowngradeAction)(0), // 7: etcdserverpb.DowngradeRequest.DowngradeAction
+ (*ResponseHeader)(nil), // 8: etcdserverpb.ResponseHeader
+ (*RangeRequest)(nil), // 9: etcdserverpb.RangeRequest
+ (*RangeResponse)(nil), // 10: etcdserverpb.RangeResponse
+ (*PutRequest)(nil), // 11: etcdserverpb.PutRequest
+ (*PutResponse)(nil), // 12: etcdserverpb.PutResponse
+ (*DeleteRangeRequest)(nil), // 13: etcdserverpb.DeleteRangeRequest
+ (*DeleteRangeResponse)(nil), // 14: etcdserverpb.DeleteRangeResponse
+ (*RequestOp)(nil), // 15: etcdserverpb.RequestOp
+ (*ResponseOp)(nil), // 16: etcdserverpb.ResponseOp
+ (*Compare)(nil), // 17: etcdserverpb.Compare
+ (*TxnRequest)(nil), // 18: etcdserverpb.TxnRequest
+ (*TxnResponse)(nil), // 19: etcdserverpb.TxnResponse
+ (*CompactionRequest)(nil), // 20: etcdserverpb.CompactionRequest
+ (*CompactionResponse)(nil), // 21: etcdserverpb.CompactionResponse
+ (*HashRequest)(nil), // 22: etcdserverpb.HashRequest
+ (*HashKVRequest)(nil), // 23: etcdserverpb.HashKVRequest
+ (*HashKVResponse)(nil), // 24: etcdserverpb.HashKVResponse
+ (*HashResponse)(nil), // 25: etcdserverpb.HashResponse
+ (*SnapshotRequest)(nil), // 26: etcdserverpb.SnapshotRequest
+ (*SnapshotResponse)(nil), // 27: etcdserverpb.SnapshotResponse
+ (*WatchRequest)(nil), // 28: etcdserverpb.WatchRequest
+ (*WatchCreateRequest)(nil), // 29: etcdserverpb.WatchCreateRequest
+ (*WatchCancelRequest)(nil), // 30: etcdserverpb.WatchCancelRequest
+ (*WatchProgressRequest)(nil), // 31: etcdserverpb.WatchProgressRequest
+ (*WatchResponse)(nil), // 32: etcdserverpb.WatchResponse
+ (*LeaseGrantRequest)(nil), // 33: etcdserverpb.LeaseGrantRequest
+ (*LeaseGrantResponse)(nil), // 34: etcdserverpb.LeaseGrantResponse
+ (*LeaseRevokeRequest)(nil), // 35: etcdserverpb.LeaseRevokeRequest
+ (*LeaseRevokeResponse)(nil), // 36: etcdserverpb.LeaseRevokeResponse
+ (*LeaseCheckpoint)(nil), // 37: etcdserverpb.LeaseCheckpoint
+ (*LeaseCheckpointRequest)(nil), // 38: etcdserverpb.LeaseCheckpointRequest
+ (*LeaseCheckpointResponse)(nil), // 39: etcdserverpb.LeaseCheckpointResponse
+ (*LeaseKeepAliveRequest)(nil), // 40: etcdserverpb.LeaseKeepAliveRequest
+ (*LeaseKeepAliveResponse)(nil), // 41: etcdserverpb.LeaseKeepAliveResponse
+ (*LeaseTimeToLiveRequest)(nil), // 42: etcdserverpb.LeaseTimeToLiveRequest
+ (*LeaseTimeToLiveResponse)(nil), // 43: etcdserverpb.LeaseTimeToLiveResponse
+ (*LeaseLeasesRequest)(nil), // 44: etcdserverpb.LeaseLeasesRequest
+ (*LeaseStatus)(nil), // 45: etcdserverpb.LeaseStatus
+ (*LeaseLeasesResponse)(nil), // 46: etcdserverpb.LeaseLeasesResponse
+ (*Member)(nil), // 47: etcdserverpb.Member
+ (*MemberAddRequest)(nil), // 48: etcdserverpb.MemberAddRequest
+ (*MemberAddResponse)(nil), // 49: etcdserverpb.MemberAddResponse
+ (*MemberRemoveRequest)(nil), // 50: etcdserverpb.MemberRemoveRequest
+ (*MemberRemoveResponse)(nil), // 51: etcdserverpb.MemberRemoveResponse
+ (*MemberUpdateRequest)(nil), // 52: etcdserverpb.MemberUpdateRequest
+ (*MemberUpdateResponse)(nil), // 53: etcdserverpb.MemberUpdateResponse
+ (*MemberListRequest)(nil), // 54: etcdserverpb.MemberListRequest
+ (*MemberListResponse)(nil), // 55: etcdserverpb.MemberListResponse
+ (*MemberPromoteRequest)(nil), // 56: etcdserverpb.MemberPromoteRequest
+ (*MemberPromoteResponse)(nil), // 57: etcdserverpb.MemberPromoteResponse
+ (*DefragmentRequest)(nil), // 58: etcdserverpb.DefragmentRequest
+ (*DefragmentResponse)(nil), // 59: etcdserverpb.DefragmentResponse
+ (*MoveLeaderRequest)(nil), // 60: etcdserverpb.MoveLeaderRequest
+ (*MoveLeaderResponse)(nil), // 61: etcdserverpb.MoveLeaderResponse
+ (*AlarmRequest)(nil), // 62: etcdserverpb.AlarmRequest
+ (*AlarmMember)(nil), // 63: etcdserverpb.AlarmMember
+ (*AlarmResponse)(nil), // 64: etcdserverpb.AlarmResponse
+ (*DowngradeRequest)(nil), // 65: etcdserverpb.DowngradeRequest
+ (*DowngradeResponse)(nil), // 66: etcdserverpb.DowngradeResponse
+ (*DowngradeVersionTestRequest)(nil), // 67: etcdserverpb.DowngradeVersionTestRequest
+ (*StatusRequest)(nil), // 68: etcdserverpb.StatusRequest
+ (*StatusResponse)(nil), // 69: etcdserverpb.StatusResponse
+ (*DowngradeInfo)(nil), // 70: etcdserverpb.DowngradeInfo
+ (*AuthEnableRequest)(nil), // 71: etcdserverpb.AuthEnableRequest
+ (*AuthDisableRequest)(nil), // 72: etcdserverpb.AuthDisableRequest
+ (*AuthStatusRequest)(nil), // 73: etcdserverpb.AuthStatusRequest
+ (*AuthenticateRequest)(nil), // 74: etcdserverpb.AuthenticateRequest
+ (*AuthUserAddRequest)(nil), // 75: etcdserverpb.AuthUserAddRequest
+ (*AuthUserGetRequest)(nil), // 76: etcdserverpb.AuthUserGetRequest
+ (*AuthUserDeleteRequest)(nil), // 77: etcdserverpb.AuthUserDeleteRequest
+ (*AuthUserChangePasswordRequest)(nil), // 78: etcdserverpb.AuthUserChangePasswordRequest
+ (*AuthUserGrantRoleRequest)(nil), // 79: etcdserverpb.AuthUserGrantRoleRequest
+ (*AuthUserRevokeRoleRequest)(nil), // 80: etcdserverpb.AuthUserRevokeRoleRequest
+ (*AuthRoleAddRequest)(nil), // 81: etcdserverpb.AuthRoleAddRequest
+ (*AuthRoleGetRequest)(nil), // 82: etcdserverpb.AuthRoleGetRequest
+ (*AuthUserListRequest)(nil), // 83: etcdserverpb.AuthUserListRequest
+ (*AuthRoleListRequest)(nil), // 84: etcdserverpb.AuthRoleListRequest
+ (*AuthRoleDeleteRequest)(nil), // 85: etcdserverpb.AuthRoleDeleteRequest
+ (*AuthRoleGrantPermissionRequest)(nil), // 86: etcdserverpb.AuthRoleGrantPermissionRequest
+ (*AuthRoleRevokePermissionRequest)(nil), // 87: etcdserverpb.AuthRoleRevokePermissionRequest
+ (*AuthEnableResponse)(nil), // 88: etcdserverpb.AuthEnableResponse
+ (*AuthDisableResponse)(nil), // 89: etcdserverpb.AuthDisableResponse
+ (*AuthStatusResponse)(nil), // 90: etcdserverpb.AuthStatusResponse
+ (*AuthenticateResponse)(nil), // 91: etcdserverpb.AuthenticateResponse
+ (*AuthUserAddResponse)(nil), // 92: etcdserverpb.AuthUserAddResponse
+ (*AuthUserGetResponse)(nil), // 93: etcdserverpb.AuthUserGetResponse
+ (*AuthUserDeleteResponse)(nil), // 94: etcdserverpb.AuthUserDeleteResponse
+ (*AuthUserChangePasswordResponse)(nil), // 95: etcdserverpb.AuthUserChangePasswordResponse
+ (*AuthUserGrantRoleResponse)(nil), // 96: etcdserverpb.AuthUserGrantRoleResponse
+ (*AuthUserRevokeRoleResponse)(nil), // 97: etcdserverpb.AuthUserRevokeRoleResponse
+ (*AuthRoleAddResponse)(nil), // 98: etcdserverpb.AuthRoleAddResponse
+ (*AuthRoleGetResponse)(nil), // 99: etcdserverpb.AuthRoleGetResponse
+ (*AuthRoleListResponse)(nil), // 100: etcdserverpb.AuthRoleListResponse
+ (*AuthUserListResponse)(nil), // 101: etcdserverpb.AuthUserListResponse
+ (*AuthRoleDeleteResponse)(nil), // 102: etcdserverpb.AuthRoleDeleteResponse
+ (*AuthRoleGrantPermissionResponse)(nil), // 103: etcdserverpb.AuthRoleGrantPermissionResponse
+ (*AuthRoleRevokePermissionResponse)(nil), // 104: etcdserverpb.AuthRoleRevokePermissionResponse
+ (*RangeStreamResponse)(nil), // 105: etcdserverpb.RangeStreamResponse
+ (*mvccpb.KeyValue)(nil), // 106: mvccpb.KeyValue
+ (*mvccpb.Event)(nil), // 107: mvccpb.Event
+ (*authpb.UserAddOptions)(nil), // 108: authpb.UserAddOptions
+ (*authpb.Permission)(nil), // 109: authpb.Permission
+}
+var file_rpc_proto_depIdxs = []int32{
+ 1, // 0: etcdserverpb.RangeRequest.sort_order:type_name -> etcdserverpb.RangeRequest.SortOrder
+ 2, // 1: etcdserverpb.RangeRequest.sort_target:type_name -> etcdserverpb.RangeRequest.SortTarget
+ 8, // 2: etcdserverpb.RangeResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 106, // 3: etcdserverpb.RangeResponse.kvs:type_name -> mvccpb.KeyValue
+ 8, // 4: etcdserverpb.PutResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 106, // 5: etcdserverpb.PutResponse.prev_kv:type_name -> mvccpb.KeyValue
+ 8, // 6: etcdserverpb.DeleteRangeResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 106, // 7: etcdserverpb.DeleteRangeResponse.prev_kvs:type_name -> mvccpb.KeyValue
+ 9, // 8: etcdserverpb.RequestOp.request_range:type_name -> etcdserverpb.RangeRequest
+ 11, // 9: etcdserverpb.RequestOp.request_put:type_name -> etcdserverpb.PutRequest
+ 13, // 10: etcdserverpb.RequestOp.request_delete_range:type_name -> etcdserverpb.DeleteRangeRequest
+ 18, // 11: etcdserverpb.RequestOp.request_txn:type_name -> etcdserverpb.TxnRequest
+ 10, // 12: etcdserverpb.ResponseOp.response_range:type_name -> etcdserverpb.RangeResponse
+ 12, // 13: etcdserverpb.ResponseOp.response_put:type_name -> etcdserverpb.PutResponse
+ 14, // 14: etcdserverpb.ResponseOp.response_delete_range:type_name -> etcdserverpb.DeleteRangeResponse
+ 19, // 15: etcdserverpb.ResponseOp.response_txn:type_name -> etcdserverpb.TxnResponse
+ 3, // 16: etcdserverpb.Compare.result:type_name -> etcdserverpb.Compare.CompareResult
+ 4, // 17: etcdserverpb.Compare.target:type_name -> etcdserverpb.Compare.CompareTarget
+ 17, // 18: etcdserverpb.TxnRequest.compare:type_name -> etcdserverpb.Compare
+ 15, // 19: etcdserverpb.TxnRequest.success:type_name -> etcdserverpb.RequestOp
+ 15, // 20: etcdserverpb.TxnRequest.failure:type_name -> etcdserverpb.RequestOp
+ 8, // 21: etcdserverpb.TxnResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 16, // 22: etcdserverpb.TxnResponse.responses:type_name -> etcdserverpb.ResponseOp
+ 8, // 23: etcdserverpb.CompactionResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 24: etcdserverpb.HashKVResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 25: etcdserverpb.HashResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 26: etcdserverpb.SnapshotResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 29, // 27: etcdserverpb.WatchRequest.create_request:type_name -> etcdserverpb.WatchCreateRequest
+ 30, // 28: etcdserverpb.WatchRequest.cancel_request:type_name -> etcdserverpb.WatchCancelRequest
+ 31, // 29: etcdserverpb.WatchRequest.progress_request:type_name -> etcdserverpb.WatchProgressRequest
+ 5, // 30: etcdserverpb.WatchCreateRequest.filters:type_name -> etcdserverpb.WatchCreateRequest.FilterType
+ 8, // 31: etcdserverpb.WatchResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 107, // 32: etcdserverpb.WatchResponse.events:type_name -> mvccpb.Event
+ 8, // 33: etcdserverpb.LeaseGrantResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 34: etcdserverpb.LeaseRevokeResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 37, // 35: etcdserverpb.LeaseCheckpointRequest.checkpoints:type_name -> etcdserverpb.LeaseCheckpoint
+ 8, // 36: etcdserverpb.LeaseCheckpointResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 37: etcdserverpb.LeaseKeepAliveResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 38: etcdserverpb.LeaseTimeToLiveResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 39: etcdserverpb.LeaseLeasesResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 45, // 40: etcdserverpb.LeaseLeasesResponse.leases:type_name -> etcdserverpb.LeaseStatus
+ 8, // 41: etcdserverpb.MemberAddResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 47, // 42: etcdserverpb.MemberAddResponse.member:type_name -> etcdserverpb.Member
+ 47, // 43: etcdserverpb.MemberAddResponse.members:type_name -> etcdserverpb.Member
+ 8, // 44: etcdserverpb.MemberRemoveResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 47, // 45: etcdserverpb.MemberRemoveResponse.members:type_name -> etcdserverpb.Member
+ 8, // 46: etcdserverpb.MemberUpdateResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 47, // 47: etcdserverpb.MemberUpdateResponse.members:type_name -> etcdserverpb.Member
+ 8, // 48: etcdserverpb.MemberListResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 47, // 49: etcdserverpb.MemberListResponse.members:type_name -> etcdserverpb.Member
+ 8, // 50: etcdserverpb.MemberPromoteResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 47, // 51: etcdserverpb.MemberPromoteResponse.members:type_name -> etcdserverpb.Member
+ 8, // 52: etcdserverpb.DefragmentResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 53: etcdserverpb.MoveLeaderResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 6, // 54: etcdserverpb.AlarmRequest.action:type_name -> etcdserverpb.AlarmRequest.AlarmAction
+ 0, // 55: etcdserverpb.AlarmRequest.alarm:type_name -> etcdserverpb.AlarmType
+ 0, // 56: etcdserverpb.AlarmMember.alarm:type_name -> etcdserverpb.AlarmType
+ 8, // 57: etcdserverpb.AlarmResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 63, // 58: etcdserverpb.AlarmResponse.alarms:type_name -> etcdserverpb.AlarmMember
+ 7, // 59: etcdserverpb.DowngradeRequest.action:type_name -> etcdserverpb.DowngradeRequest.DowngradeAction
+ 8, // 60: etcdserverpb.DowngradeResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 61: etcdserverpb.StatusResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 70, // 62: etcdserverpb.StatusResponse.downgradeInfo:type_name -> etcdserverpb.DowngradeInfo
+ 108, // 63: etcdserverpb.AuthUserAddRequest.options:type_name -> authpb.UserAddOptions
+ 109, // 64: etcdserverpb.AuthRoleGrantPermissionRequest.perm:type_name -> authpb.Permission
+ 8, // 65: etcdserverpb.AuthEnableResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 66: etcdserverpb.AuthDisableResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 67: etcdserverpb.AuthStatusResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 68: etcdserverpb.AuthenticateResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 69: etcdserverpb.AuthUserAddResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 70: etcdserverpb.AuthUserGetResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 71: etcdserverpb.AuthUserDeleteResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 72: etcdserverpb.AuthUserChangePasswordResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 73: etcdserverpb.AuthUserGrantRoleResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 74: etcdserverpb.AuthUserRevokeRoleResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 75: etcdserverpb.AuthRoleAddResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 76: etcdserverpb.AuthRoleGetResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 109, // 77: etcdserverpb.AuthRoleGetResponse.perm:type_name -> authpb.Permission
+ 8, // 78: etcdserverpb.AuthRoleListResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 79: etcdserverpb.AuthUserListResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 80: etcdserverpb.AuthRoleDeleteResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 81: etcdserverpb.AuthRoleGrantPermissionResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 8, // 82: etcdserverpb.AuthRoleRevokePermissionResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 10, // 83: etcdserverpb.RangeStreamResponse.range_response:type_name -> etcdserverpb.RangeResponse
+ 9, // 84: etcdserverpb.KV.Range:input_type -> etcdserverpb.RangeRequest
+ 9, // 85: etcdserverpb.KV.RangeStream:input_type -> etcdserverpb.RangeRequest
+ 11, // 86: etcdserverpb.KV.Put:input_type -> etcdserverpb.PutRequest
+ 13, // 87: etcdserverpb.KV.DeleteRange:input_type -> etcdserverpb.DeleteRangeRequest
+ 18, // 88: etcdserverpb.KV.Txn:input_type -> etcdserverpb.TxnRequest
+ 20, // 89: etcdserverpb.KV.Compact:input_type -> etcdserverpb.CompactionRequest
+ 28, // 90: etcdserverpb.Watch.Watch:input_type -> etcdserverpb.WatchRequest
+ 33, // 91: etcdserverpb.Lease.LeaseGrant:input_type -> etcdserverpb.LeaseGrantRequest
+ 35, // 92: etcdserverpb.Lease.LeaseRevoke:input_type -> etcdserverpb.LeaseRevokeRequest
+ 40, // 93: etcdserverpb.Lease.LeaseKeepAlive:input_type -> etcdserverpb.LeaseKeepAliveRequest
+ 42, // 94: etcdserverpb.Lease.LeaseTimeToLive:input_type -> etcdserverpb.LeaseTimeToLiveRequest
+ 44, // 95: etcdserverpb.Lease.LeaseLeases:input_type -> etcdserverpb.LeaseLeasesRequest
+ 48, // 96: etcdserverpb.Cluster.MemberAdd:input_type -> etcdserverpb.MemberAddRequest
+ 50, // 97: etcdserverpb.Cluster.MemberRemove:input_type -> etcdserverpb.MemberRemoveRequest
+ 52, // 98: etcdserverpb.Cluster.MemberUpdate:input_type -> etcdserverpb.MemberUpdateRequest
+ 54, // 99: etcdserverpb.Cluster.MemberList:input_type -> etcdserverpb.MemberListRequest
+ 56, // 100: etcdserverpb.Cluster.MemberPromote:input_type -> etcdserverpb.MemberPromoteRequest
+ 62, // 101: etcdserverpb.Maintenance.Alarm:input_type -> etcdserverpb.AlarmRequest
+ 68, // 102: etcdserverpb.Maintenance.Status:input_type -> etcdserverpb.StatusRequest
+ 58, // 103: etcdserverpb.Maintenance.Defragment:input_type -> etcdserverpb.DefragmentRequest
+ 22, // 104: etcdserverpb.Maintenance.Hash:input_type -> etcdserverpb.HashRequest
+ 23, // 105: etcdserverpb.Maintenance.HashKV:input_type -> etcdserverpb.HashKVRequest
+ 26, // 106: etcdserverpb.Maintenance.Snapshot:input_type -> etcdserverpb.SnapshotRequest
+ 60, // 107: etcdserverpb.Maintenance.MoveLeader:input_type -> etcdserverpb.MoveLeaderRequest
+ 65, // 108: etcdserverpb.Maintenance.Downgrade:input_type -> etcdserverpb.DowngradeRequest
+ 71, // 109: etcdserverpb.Auth.AuthEnable:input_type -> etcdserverpb.AuthEnableRequest
+ 72, // 110: etcdserverpb.Auth.AuthDisable:input_type -> etcdserverpb.AuthDisableRequest
+ 73, // 111: etcdserverpb.Auth.AuthStatus:input_type -> etcdserverpb.AuthStatusRequest
+ 74, // 112: etcdserverpb.Auth.Authenticate:input_type -> etcdserverpb.AuthenticateRequest
+ 75, // 113: etcdserverpb.Auth.UserAdd:input_type -> etcdserverpb.AuthUserAddRequest
+ 76, // 114: etcdserverpb.Auth.UserGet:input_type -> etcdserverpb.AuthUserGetRequest
+ 83, // 115: etcdserverpb.Auth.UserList:input_type -> etcdserverpb.AuthUserListRequest
+ 77, // 116: etcdserverpb.Auth.UserDelete:input_type -> etcdserverpb.AuthUserDeleteRequest
+ 78, // 117: etcdserverpb.Auth.UserChangePassword:input_type -> etcdserverpb.AuthUserChangePasswordRequest
+ 79, // 118: etcdserverpb.Auth.UserGrantRole:input_type -> etcdserverpb.AuthUserGrantRoleRequest
+ 80, // 119: etcdserverpb.Auth.UserRevokeRole:input_type -> etcdserverpb.AuthUserRevokeRoleRequest
+ 81, // 120: etcdserverpb.Auth.RoleAdd:input_type -> etcdserverpb.AuthRoleAddRequest
+ 82, // 121: etcdserverpb.Auth.RoleGet:input_type -> etcdserverpb.AuthRoleGetRequest
+ 84, // 122: etcdserverpb.Auth.RoleList:input_type -> etcdserverpb.AuthRoleListRequest
+ 85, // 123: etcdserverpb.Auth.RoleDelete:input_type -> etcdserverpb.AuthRoleDeleteRequest
+ 86, // 124: etcdserverpb.Auth.RoleGrantPermission:input_type -> etcdserverpb.AuthRoleGrantPermissionRequest
+ 87, // 125: etcdserverpb.Auth.RoleRevokePermission:input_type -> etcdserverpb.AuthRoleRevokePermissionRequest
+ 10, // 126: etcdserverpb.KV.Range:output_type -> etcdserverpb.RangeResponse
+ 105, // 127: etcdserverpb.KV.RangeStream:output_type -> etcdserverpb.RangeStreamResponse
+ 12, // 128: etcdserverpb.KV.Put:output_type -> etcdserverpb.PutResponse
+ 14, // 129: etcdserverpb.KV.DeleteRange:output_type -> etcdserverpb.DeleteRangeResponse
+ 19, // 130: etcdserverpb.KV.Txn:output_type -> etcdserverpb.TxnResponse
+ 21, // 131: etcdserverpb.KV.Compact:output_type -> etcdserverpb.CompactionResponse
+ 32, // 132: etcdserverpb.Watch.Watch:output_type -> etcdserverpb.WatchResponse
+ 34, // 133: etcdserverpb.Lease.LeaseGrant:output_type -> etcdserverpb.LeaseGrantResponse
+ 36, // 134: etcdserverpb.Lease.LeaseRevoke:output_type -> etcdserverpb.LeaseRevokeResponse
+ 41, // 135: etcdserverpb.Lease.LeaseKeepAlive:output_type -> etcdserverpb.LeaseKeepAliveResponse
+ 43, // 136: etcdserverpb.Lease.LeaseTimeToLive:output_type -> etcdserverpb.LeaseTimeToLiveResponse
+ 46, // 137: etcdserverpb.Lease.LeaseLeases:output_type -> etcdserverpb.LeaseLeasesResponse
+ 49, // 138: etcdserverpb.Cluster.MemberAdd:output_type -> etcdserverpb.MemberAddResponse
+ 51, // 139: etcdserverpb.Cluster.MemberRemove:output_type -> etcdserverpb.MemberRemoveResponse
+ 53, // 140: etcdserverpb.Cluster.MemberUpdate:output_type -> etcdserverpb.MemberUpdateResponse
+ 55, // 141: etcdserverpb.Cluster.MemberList:output_type -> etcdserverpb.MemberListResponse
+ 57, // 142: etcdserverpb.Cluster.MemberPromote:output_type -> etcdserverpb.MemberPromoteResponse
+ 64, // 143: etcdserverpb.Maintenance.Alarm:output_type -> etcdserverpb.AlarmResponse
+ 69, // 144: etcdserverpb.Maintenance.Status:output_type -> etcdserverpb.StatusResponse
+ 59, // 145: etcdserverpb.Maintenance.Defragment:output_type -> etcdserverpb.DefragmentResponse
+ 25, // 146: etcdserverpb.Maintenance.Hash:output_type -> etcdserverpb.HashResponse
+ 24, // 147: etcdserverpb.Maintenance.HashKV:output_type -> etcdserverpb.HashKVResponse
+ 27, // 148: etcdserverpb.Maintenance.Snapshot:output_type -> etcdserverpb.SnapshotResponse
+ 61, // 149: etcdserverpb.Maintenance.MoveLeader:output_type -> etcdserverpb.MoveLeaderResponse
+ 66, // 150: etcdserverpb.Maintenance.Downgrade:output_type -> etcdserverpb.DowngradeResponse
+ 88, // 151: etcdserverpb.Auth.AuthEnable:output_type -> etcdserverpb.AuthEnableResponse
+ 89, // 152: etcdserverpb.Auth.AuthDisable:output_type -> etcdserverpb.AuthDisableResponse
+ 90, // 153: etcdserverpb.Auth.AuthStatus:output_type -> etcdserverpb.AuthStatusResponse
+ 91, // 154: etcdserverpb.Auth.Authenticate:output_type -> etcdserverpb.AuthenticateResponse
+ 92, // 155: etcdserverpb.Auth.UserAdd:output_type -> etcdserverpb.AuthUserAddResponse
+ 93, // 156: etcdserverpb.Auth.UserGet:output_type -> etcdserverpb.AuthUserGetResponse
+ 101, // 157: etcdserverpb.Auth.UserList:output_type -> etcdserverpb.AuthUserListResponse
+ 94, // 158: etcdserverpb.Auth.UserDelete:output_type -> etcdserverpb.AuthUserDeleteResponse
+ 95, // 159: etcdserverpb.Auth.UserChangePassword:output_type -> etcdserverpb.AuthUserChangePasswordResponse
+ 96, // 160: etcdserverpb.Auth.UserGrantRole:output_type -> etcdserverpb.AuthUserGrantRoleResponse
+ 97, // 161: etcdserverpb.Auth.UserRevokeRole:output_type -> etcdserverpb.AuthUserRevokeRoleResponse
+ 98, // 162: etcdserverpb.Auth.RoleAdd:output_type -> etcdserverpb.AuthRoleAddResponse
+ 99, // 163: etcdserverpb.Auth.RoleGet:output_type -> etcdserverpb.AuthRoleGetResponse
+ 100, // 164: etcdserverpb.Auth.RoleList:output_type -> etcdserverpb.AuthRoleListResponse
+ 102, // 165: etcdserverpb.Auth.RoleDelete:output_type -> etcdserverpb.AuthRoleDeleteResponse
+ 103, // 166: etcdserverpb.Auth.RoleGrantPermission:output_type -> etcdserverpb.AuthRoleGrantPermissionResponse
+ 104, // 167: etcdserverpb.Auth.RoleRevokePermission:output_type -> etcdserverpb.AuthRoleRevokePermissionResponse
+ 126, // [126:168] is the sub-list for method output_type
+ 84, // [84:126] is the sub-list for method input_type
+ 84, // [84:84] is the sub-list for extension type_name
+ 84, // [84:84] is the sub-list for extension extendee
+ 0, // [0:84] is the sub-list for field type_name
+}
+
+func init() { file_rpc_proto_init() }
+func file_rpc_proto_init() {
+ if File_rpc_proto != nil {
+ return
+ }
+ file_rpc_proto_msgTypes[7].OneofWrappers = []any{
+ (*RequestOp_RequestRange)(nil),
+ (*RequestOp_RequestPut)(nil),
+ (*RequestOp_RequestDeleteRange)(nil),
+ (*RequestOp_RequestTxn)(nil),
}
- if interceptor == nil {
- return srv.(LeaseServer).LeaseLeases(ctx, in)
+ file_rpc_proto_msgTypes[8].OneofWrappers = []any{
+ (*ResponseOp_ResponseRange)(nil),
+ (*ResponseOp_ResponsePut)(nil),
+ (*ResponseOp_ResponseDeleteRange)(nil),
+ (*ResponseOp_ResponseTxn)(nil),
}
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Lease/LeaseLeases",
+ file_rpc_proto_msgTypes[9].OneofWrappers = []any{
+ (*Compare_Version)(nil),
+ (*Compare_CreateRevision)(nil),
+ (*Compare_ModRevision)(nil),
+ (*Compare_Value)(nil),
+ (*Compare_Lease)(nil),
}
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LeaseServer).LeaseLeases(ctx, req.(*LeaseLeasesRequest))
+ file_rpc_proto_msgTypes[20].OneofWrappers = []any{
+ (*WatchRequest_CreateRequest)(nil),
+ (*WatchRequest_CancelRequest)(nil),
+ (*WatchRequest_ProgressRequest)(nil),
}
- return interceptor(ctx, in, info, handler)
-}
-
-var _Lease_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.Lease",
- HandlerType: (*LeaseServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "LeaseGrant",
- Handler: _Lease_LeaseGrant_Handler,
- },
- {
- MethodName: "LeaseRevoke",
- Handler: _Lease_LeaseRevoke_Handler,
- },
- {
- MethodName: "LeaseTimeToLive",
- Handler: _Lease_LeaseTimeToLive_Handler,
- },
- {
- MethodName: "LeaseLeases",
- Handler: _Lease_LeaseLeases_Handler,
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_rpc_proto_rawDesc), len(file_rpc_proto_rawDesc)),
+ NumEnums: 8,
+ NumMessages: 98,
+ NumExtensions: 0,
+ NumServices: 6,
},
- },
- Streams: []grpc.StreamDesc{
- {
- StreamName: "LeaseKeepAlive",
- Handler: _Lease_LeaseKeepAlive_Handler,
- ServerStreams: true,
- ClientStreams: true,
- },
- },
- Metadata: "rpc.proto",
-}
-
-// ClusterClient is the client API for Cluster service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type ClusterClient interface {
- // MemberAdd adds a member into the cluster.
- MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error)
- // MemberRemove removes an existing member from the cluster.
- MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error)
- // MemberUpdate updates the member configuration.
- MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error)
- // MemberList lists all the members in the cluster.
- MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error)
- // MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
- MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error)
-}
-
-type clusterClient struct {
- cc *grpc.ClientConn
-}
-
-func NewClusterClient(cc *grpc.ClientConn) ClusterClient {
- return &clusterClient{cc}
-}
-
-func (c *clusterClient) MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error) {
- out := new(MemberAddResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Cluster/MemberAdd", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *clusterClient) MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error) {
- out := new(MemberRemoveResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Cluster/MemberRemove", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *clusterClient) MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error) {
- out := new(MemberUpdateResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Cluster/MemberUpdate", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *clusterClient) MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error) {
- out := new(MemberListResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Cluster/MemberList", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *clusterClient) MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error) {
- out := new(MemberPromoteResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Cluster/MemberPromote", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// ClusterServer is the server API for Cluster service.
-type ClusterServer interface {
- // MemberAdd adds a member into the cluster.
- MemberAdd(context.Context, *MemberAddRequest) (*MemberAddResponse, error)
- // MemberRemove removes an existing member from the cluster.
- MemberRemove(context.Context, *MemberRemoveRequest) (*MemberRemoveResponse, error)
- // MemberUpdate updates the member configuration.
- MemberUpdate(context.Context, *MemberUpdateRequest) (*MemberUpdateResponse, error)
- // MemberList lists all the members in the cluster.
- MemberList(context.Context, *MemberListRequest) (*MemberListResponse, error)
- // MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
- MemberPromote(context.Context, *MemberPromoteRequest) (*MemberPromoteResponse, error)
-}
-
-// UnimplementedClusterServer can be embedded to have forward compatible implementations.
-type UnimplementedClusterServer struct {
-}
-
-func (*UnimplementedClusterServer) MemberAdd(ctx context.Context, req *MemberAddRequest) (*MemberAddResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MemberAdd not implemented")
-}
-func (*UnimplementedClusterServer) MemberRemove(ctx context.Context, req *MemberRemoveRequest) (*MemberRemoveResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MemberRemove not implemented")
-}
-func (*UnimplementedClusterServer) MemberUpdate(ctx context.Context, req *MemberUpdateRequest) (*MemberUpdateResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MemberUpdate not implemented")
-}
-func (*UnimplementedClusterServer) MemberList(ctx context.Context, req *MemberListRequest) (*MemberListResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MemberList not implemented")
-}
-func (*UnimplementedClusterServer) MemberPromote(ctx context.Context, req *MemberPromoteRequest) (*MemberPromoteResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MemberPromote not implemented")
-}
-
-func RegisterClusterServer(s *grpc.Server, srv ClusterServer) {
- s.RegisterService(&_Cluster_serviceDesc, srv)
-}
-
-func _Cluster_MemberAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MemberAddRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(ClusterServer).MemberAdd(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Cluster/MemberAdd",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(ClusterServer).MemberAdd(ctx, req.(*MemberAddRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Cluster_MemberRemove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MemberRemoveRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(ClusterServer).MemberRemove(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Cluster/MemberRemove",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(ClusterServer).MemberRemove(ctx, req.(*MemberRemoveRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Cluster_MemberUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MemberUpdateRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(ClusterServer).MemberUpdate(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Cluster/MemberUpdate",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(ClusterServer).MemberUpdate(ctx, req.(*MemberUpdateRequest))
- }
- return interceptor(ctx, in, info, handler)
+ GoTypes: file_rpc_proto_goTypes,
+ DependencyIndexes: file_rpc_proto_depIdxs,
+ EnumInfos: file_rpc_proto_enumTypes,
+ MessageInfos: file_rpc_proto_msgTypes,
+ }.Build()
+ File_rpc_proto = out.File
+ file_rpc_proto_goTypes = nil
+ file_rpc_proto_depIdxs = nil
}
-
-func _Cluster_MemberList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MemberListRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(ClusterServer).MemberList(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Cluster/MemberList",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(ClusterServer).MemberList(ctx, req.(*MemberListRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Cluster_MemberPromote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MemberPromoteRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(ClusterServer).MemberPromote(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Cluster/MemberPromote",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(ClusterServer).MemberPromote(ctx, req.(*MemberPromoteRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-var _Cluster_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.Cluster",
- HandlerType: (*ClusterServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "MemberAdd",
- Handler: _Cluster_MemberAdd_Handler,
- },
- {
- MethodName: "MemberRemove",
- Handler: _Cluster_MemberRemove_Handler,
- },
- {
- MethodName: "MemberUpdate",
- Handler: _Cluster_MemberUpdate_Handler,
- },
- {
- MethodName: "MemberList",
- Handler: _Cluster_MemberList_Handler,
- },
- {
- MethodName: "MemberPromote",
- Handler: _Cluster_MemberPromote_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "rpc.proto",
-}
-
-// MaintenanceClient is the client API for Maintenance service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type MaintenanceClient interface {
- // Alarm activates, deactivates, and queries alarms regarding cluster health.
- Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error)
- // Status gets the status of the member.
- Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
- // Defragment defragments a member's backend database to recover storage space.
- Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error)
- // Hash computes the hash of whole backend keyspace,
- // including key, lease, and other buckets in storage.
- // This is designed for testing ONLY!
- // Do not rely on this in production with ongoing transactions,
- // since Hash operation does not hold MVCC locks.
- // Use "HashKV" API instead for "key" bucket consistency checks.
- Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error)
- // HashKV computes the hash of all MVCC keys up to a given revision.
- // It only iterates "key" bucket in backend storage.
- HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error)
- // Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
- Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (Maintenance_SnapshotClient, error)
- // MoveLeader requests current leader node to transfer its leadership to transferee.
- MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error)
- // Downgrade requests downgrades, verifies feasibility or cancels downgrade
- // on the cluster version.
- // Supported since etcd 3.5.
- Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error)
-}
-
-type maintenanceClient struct {
- cc *grpc.ClientConn
-}
-
-func NewMaintenanceClient(cc *grpc.ClientConn) MaintenanceClient {
- return &maintenanceClient{cc}
-}
-
-func (c *maintenanceClient) Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error) {
- out := new(AlarmResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/Alarm", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
- out := new(StatusResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/Status", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error) {
- out := new(DefragmentResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/Defragment", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error) {
- out := new(HashResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/Hash", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error) {
- out := new(HashKVResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/HashKV", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (Maintenance_SnapshotClient, error) {
- stream, err := c.cc.NewStream(ctx, &_Maintenance_serviceDesc.Streams[0], "/etcdserverpb.Maintenance/Snapshot", opts...)
- if err != nil {
- return nil, err
- }
- x := &maintenanceSnapshotClient{stream}
- if err := x.ClientStream.SendMsg(in); err != nil {
- return nil, err
- }
- if err := x.ClientStream.CloseSend(); err != nil {
- return nil, err
- }
- return x, nil
-}
-
-type Maintenance_SnapshotClient interface {
- Recv() (*SnapshotResponse, error)
- grpc.ClientStream
-}
-
-type maintenanceSnapshotClient struct {
- grpc.ClientStream
-}
-
-func (x *maintenanceSnapshotClient) Recv() (*SnapshotResponse, error) {
- m := new(SnapshotResponse)
- if err := x.ClientStream.RecvMsg(m); err != nil {
- return nil, err
- }
- return m, nil
-}
-
-func (c *maintenanceClient) MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error) {
- out := new(MoveLeaderResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/MoveLeader", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *maintenanceClient) Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error) {
- out := new(DowngradeResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Maintenance/Downgrade", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// MaintenanceServer is the server API for Maintenance service.
-type MaintenanceServer interface {
- // Alarm activates, deactivates, and queries alarms regarding cluster health.
- Alarm(context.Context, *AlarmRequest) (*AlarmResponse, error)
- // Status gets the status of the member.
- Status(context.Context, *StatusRequest) (*StatusResponse, error)
- // Defragment defragments a member's backend database to recover storage space.
- Defragment(context.Context, *DefragmentRequest) (*DefragmentResponse, error)
- // Hash computes the hash of whole backend keyspace,
- // including key, lease, and other buckets in storage.
- // This is designed for testing ONLY!
- // Do not rely on this in production with ongoing transactions,
- // since Hash operation does not hold MVCC locks.
- // Use "HashKV" API instead for "key" bucket consistency checks.
- Hash(context.Context, *HashRequest) (*HashResponse, error)
- // HashKV computes the hash of all MVCC keys up to a given revision.
- // It only iterates "key" bucket in backend storage.
- HashKV(context.Context, *HashKVRequest) (*HashKVResponse, error)
- // Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
- Snapshot(*SnapshotRequest, Maintenance_SnapshotServer) error
- // MoveLeader requests current leader node to transfer its leadership to transferee.
- MoveLeader(context.Context, *MoveLeaderRequest) (*MoveLeaderResponse, error)
- // Downgrade requests downgrades, verifies feasibility or cancels downgrade
- // on the cluster version.
- // Supported since etcd 3.5.
- Downgrade(context.Context, *DowngradeRequest) (*DowngradeResponse, error)
-}
-
-// UnimplementedMaintenanceServer can be embedded to have forward compatible implementations.
-type UnimplementedMaintenanceServer struct {
-}
-
-func (*UnimplementedMaintenanceServer) Alarm(ctx context.Context, req *AlarmRequest) (*AlarmResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Alarm not implemented")
-}
-func (*UnimplementedMaintenanceServer) Status(ctx context.Context, req *StatusRequest) (*StatusResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Status not implemented")
-}
-func (*UnimplementedMaintenanceServer) Defragment(ctx context.Context, req *DefragmentRequest) (*DefragmentResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Defragment not implemented")
-}
-func (*UnimplementedMaintenanceServer) Hash(ctx context.Context, req *HashRequest) (*HashResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Hash not implemented")
-}
-func (*UnimplementedMaintenanceServer) HashKV(ctx context.Context, req *HashKVRequest) (*HashKVResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method HashKV not implemented")
-}
-func (*UnimplementedMaintenanceServer) Snapshot(req *SnapshotRequest, srv Maintenance_SnapshotServer) error {
- return status.Errorf(codes.Unimplemented, "method Snapshot not implemented")
-}
-func (*UnimplementedMaintenanceServer) MoveLeader(ctx context.Context, req *MoveLeaderRequest) (*MoveLeaderResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method MoveLeader not implemented")
-}
-func (*UnimplementedMaintenanceServer) Downgrade(ctx context.Context, req *DowngradeRequest) (*DowngradeResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Downgrade not implemented")
-}
-
-func RegisterMaintenanceServer(s *grpc.Server, srv MaintenanceServer) {
- s.RegisterService(&_Maintenance_serviceDesc, srv)
-}
-
-func _Maintenance_Alarm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AlarmRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).Alarm(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/Alarm",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).Alarm(ctx, req.(*AlarmRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(StatusRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).Status(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/Status",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).Status(ctx, req.(*StatusRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_Defragment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(DefragmentRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).Defragment(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/Defragment",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).Defragment(ctx, req.(*DefragmentRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_Hash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(HashRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).Hash(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/Hash",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).Hash(ctx, req.(*HashRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_HashKV_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(HashKVRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).HashKV(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/HashKV",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).HashKV(ctx, req.(*HashKVRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_Snapshot_Handler(srv interface{}, stream grpc.ServerStream) error {
- m := new(SnapshotRequest)
- if err := stream.RecvMsg(m); err != nil {
- return err
- }
- return srv.(MaintenanceServer).Snapshot(m, &maintenanceSnapshotServer{stream})
-}
-
-type Maintenance_SnapshotServer interface {
- Send(*SnapshotResponse) error
- grpc.ServerStream
-}
-
-type maintenanceSnapshotServer struct {
- grpc.ServerStream
-}
-
-func (x *maintenanceSnapshotServer) Send(m *SnapshotResponse) error {
- return x.ServerStream.SendMsg(m)
-}
-
-func _Maintenance_MoveLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(MoveLeaderRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).MoveLeader(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/MoveLeader",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).MoveLeader(ctx, req.(*MoveLeaderRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Maintenance_Downgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(DowngradeRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(MaintenanceServer).Downgrade(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Maintenance/Downgrade",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(MaintenanceServer).Downgrade(ctx, req.(*DowngradeRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-var _Maintenance_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.Maintenance",
- HandlerType: (*MaintenanceServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "Alarm",
- Handler: _Maintenance_Alarm_Handler,
- },
- {
- MethodName: "Status",
- Handler: _Maintenance_Status_Handler,
- },
- {
- MethodName: "Defragment",
- Handler: _Maintenance_Defragment_Handler,
- },
- {
- MethodName: "Hash",
- Handler: _Maintenance_Hash_Handler,
- },
- {
- MethodName: "HashKV",
- Handler: _Maintenance_HashKV_Handler,
- },
- {
- MethodName: "MoveLeader",
- Handler: _Maintenance_MoveLeader_Handler,
- },
- {
- MethodName: "Downgrade",
- Handler: _Maintenance_Downgrade_Handler,
- },
- },
- Streams: []grpc.StreamDesc{
- {
- StreamName: "Snapshot",
- Handler: _Maintenance_Snapshot_Handler,
- ServerStreams: true,
- },
- },
- Metadata: "rpc.proto",
-}
-
-// AuthClient is the client API for Auth service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type AuthClient interface {
- // AuthEnable enables authentication.
- AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error)
- // AuthDisable disables authentication.
- AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error)
- // AuthStatus displays authentication status.
- AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error)
- // Authenticate processes an authenticate request.
- Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error)
- // UserAdd adds a new user. User name cannot be empty.
- UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error)
- // UserGet gets detailed user information.
- UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error)
- // UserList gets a list of all users.
- UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error)
- // UserDelete deletes a specified user.
- UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error)
- // UserChangePassword changes the password of a specified user.
- UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error)
- // UserGrant grants a role to a specified user.
- UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error)
- // UserRevokeRole revokes a role of specified user.
- UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error)
- // RoleAdd adds a new role. Role name cannot be empty.
- RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error)
- // RoleGet gets detailed role information.
- RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error)
- // RoleList gets lists of all roles.
- RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error)
- // RoleDelete deletes a specified role.
- RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error)
- // RoleGrantPermission grants a permission of a specified key or range to a specified role.
- RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error)
- // RoleRevokePermission revokes a key or range permission of a specified role.
- RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error)
-}
-
-type authClient struct {
- cc *grpc.ClientConn
-}
-
-func NewAuthClient(cc *grpc.ClientConn) AuthClient {
- return &authClient{cc}
-}
-
-func (c *authClient) AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error) {
- out := new(AuthEnableResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/AuthEnable", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error) {
- out := new(AuthDisableResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/AuthDisable", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error) {
- out := new(AuthStatusResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/AuthStatus", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) {
- out := new(AuthenticateResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/Authenticate", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error) {
- out := new(AuthUserAddResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserAdd", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error) {
- out := new(AuthUserGetResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserGet", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error) {
- out := new(AuthUserListResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserList", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error) {
- out := new(AuthUserDeleteResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserDelete", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error) {
- out := new(AuthUserChangePasswordResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserChangePassword", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error) {
- out := new(AuthUserGrantRoleResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserGrantRole", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error) {
- out := new(AuthUserRevokeRoleResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/UserRevokeRole", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error) {
- out := new(AuthRoleAddResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleAdd", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error) {
- out := new(AuthRoleGetResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleGet", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error) {
- out := new(AuthRoleListResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleList", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error) {
- out := new(AuthRoleDeleteResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleDelete", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error) {
- out := new(AuthRoleGrantPermissionResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleGrantPermission", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *authClient) RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error) {
- out := new(AuthRoleRevokePermissionResponse)
- err := c.cc.Invoke(ctx, "/etcdserverpb.Auth/RoleRevokePermission", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// AuthServer is the server API for Auth service.
-type AuthServer interface {
- // AuthEnable enables authentication.
- AuthEnable(context.Context, *AuthEnableRequest) (*AuthEnableResponse, error)
- // AuthDisable disables authentication.
- AuthDisable(context.Context, *AuthDisableRequest) (*AuthDisableResponse, error)
- // AuthStatus displays authentication status.
- AuthStatus(context.Context, *AuthStatusRequest) (*AuthStatusResponse, error)
- // Authenticate processes an authenticate request.
- Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error)
- // UserAdd adds a new user. User name cannot be empty.
- UserAdd(context.Context, *AuthUserAddRequest) (*AuthUserAddResponse, error)
- // UserGet gets detailed user information.
- UserGet(context.Context, *AuthUserGetRequest) (*AuthUserGetResponse, error)
- // UserList gets a list of all users.
- UserList(context.Context, *AuthUserListRequest) (*AuthUserListResponse, error)
- // UserDelete deletes a specified user.
- UserDelete(context.Context, *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error)
- // UserChangePassword changes the password of a specified user.
- UserChangePassword(context.Context, *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error)
- // UserGrant grants a role to a specified user.
- UserGrantRole(context.Context, *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error)
- // UserRevokeRole revokes a role of specified user.
- UserRevokeRole(context.Context, *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error)
- // RoleAdd adds a new role. Role name cannot be empty.
- RoleAdd(context.Context, *AuthRoleAddRequest) (*AuthRoleAddResponse, error)
- // RoleGet gets detailed role information.
- RoleGet(context.Context, *AuthRoleGetRequest) (*AuthRoleGetResponse, error)
- // RoleList gets lists of all roles.
- RoleList(context.Context, *AuthRoleListRequest) (*AuthRoleListResponse, error)
- // RoleDelete deletes a specified role.
- RoleDelete(context.Context, *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error)
- // RoleGrantPermission grants a permission of a specified key or range to a specified role.
- RoleGrantPermission(context.Context, *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error)
- // RoleRevokePermission revokes a key or range permission of a specified role.
- RoleRevokePermission(context.Context, *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error)
-}
-
-// UnimplementedAuthServer can be embedded to have forward compatible implementations.
-type UnimplementedAuthServer struct {
-}
-
-func (*UnimplementedAuthServer) AuthEnable(ctx context.Context, req *AuthEnableRequest) (*AuthEnableResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method AuthEnable not implemented")
-}
-func (*UnimplementedAuthServer) AuthDisable(ctx context.Context, req *AuthDisableRequest) (*AuthDisableResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method AuthDisable not implemented")
-}
-func (*UnimplementedAuthServer) AuthStatus(ctx context.Context, req *AuthStatusRequest) (*AuthStatusResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method AuthStatus not implemented")
-}
-func (*UnimplementedAuthServer) Authenticate(ctx context.Context, req *AuthenticateRequest) (*AuthenticateResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented")
-}
-func (*UnimplementedAuthServer) UserAdd(ctx context.Context, req *AuthUserAddRequest) (*AuthUserAddResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserAdd not implemented")
-}
-func (*UnimplementedAuthServer) UserGet(ctx context.Context, req *AuthUserGetRequest) (*AuthUserGetResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserGet not implemented")
-}
-func (*UnimplementedAuthServer) UserList(ctx context.Context, req *AuthUserListRequest) (*AuthUserListResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserList not implemented")
-}
-func (*UnimplementedAuthServer) UserDelete(ctx context.Context, req *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserDelete not implemented")
-}
-func (*UnimplementedAuthServer) UserChangePassword(ctx context.Context, req *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserChangePassword not implemented")
-}
-func (*UnimplementedAuthServer) UserGrantRole(ctx context.Context, req *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserGrantRole not implemented")
-}
-func (*UnimplementedAuthServer) UserRevokeRole(ctx context.Context, req *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method UserRevokeRole not implemented")
-}
-func (*UnimplementedAuthServer) RoleAdd(ctx context.Context, req *AuthRoleAddRequest) (*AuthRoleAddResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleAdd not implemented")
-}
-func (*UnimplementedAuthServer) RoleGet(ctx context.Context, req *AuthRoleGetRequest) (*AuthRoleGetResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleGet not implemented")
-}
-func (*UnimplementedAuthServer) RoleList(ctx context.Context, req *AuthRoleListRequest) (*AuthRoleListResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleList not implemented")
-}
-func (*UnimplementedAuthServer) RoleDelete(ctx context.Context, req *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleDelete not implemented")
-}
-func (*UnimplementedAuthServer) RoleGrantPermission(ctx context.Context, req *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleGrantPermission not implemented")
-}
-func (*UnimplementedAuthServer) RoleRevokePermission(ctx context.Context, req *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method RoleRevokePermission not implemented")
-}
-
-func RegisterAuthServer(s *grpc.Server, srv AuthServer) {
- s.RegisterService(&_Auth_serviceDesc, srv)
-}
-
-func _Auth_AuthEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthEnableRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).AuthEnable(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/AuthEnable",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).AuthEnable(ctx, req.(*AuthEnableRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_AuthDisable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthDisableRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).AuthDisable(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/AuthDisable",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).AuthDisable(ctx, req.(*AuthDisableRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_AuthStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthStatusRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).AuthStatus(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/AuthStatus",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).AuthStatus(ctx, req.(*AuthStatusRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthenticateRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).Authenticate(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/Authenticate",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).Authenticate(ctx, req.(*AuthenticateRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserAddRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserAdd(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserAdd",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserAdd(ctx, req.(*AuthUserAddRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserGetRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserGet(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserGet",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserGet(ctx, req.(*AuthUserGetRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserListRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserList(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserList",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserList(ctx, req.(*AuthUserListRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserDeleteRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserDelete(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserDelete",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserDelete(ctx, req.(*AuthUserDeleteRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserChangePasswordRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserChangePassword(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserChangePassword",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserChangePassword(ctx, req.(*AuthUserChangePasswordRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserGrantRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserGrantRoleRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserGrantRole(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserGrantRole",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserGrantRole(ctx, req.(*AuthUserGrantRoleRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_UserRevokeRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthUserRevokeRoleRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).UserRevokeRole(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/UserRevokeRole",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).UserRevokeRole(ctx, req.(*AuthUserRevokeRoleRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleAddRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleAdd(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleAdd",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleAdd(ctx, req.(*AuthRoleAddRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleGetRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleGet(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleGet",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleGet(ctx, req.(*AuthRoleGetRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleListRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleList(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleList",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleList(ctx, req.(*AuthRoleListRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleDeleteRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleDelete(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleDelete",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleDelete(ctx, req.(*AuthRoleDeleteRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleGrantPermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleGrantPermissionRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleGrantPermission(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleGrantPermission",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleGrantPermission(ctx, req.(*AuthRoleGrantPermissionRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Auth_RoleRevokePermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthRoleRevokePermissionRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(AuthServer).RoleRevokePermission(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/etcdserverpb.Auth/RoleRevokePermission",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(AuthServer).RoleRevokePermission(ctx, req.(*AuthRoleRevokePermissionRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-var _Auth_serviceDesc = grpc.ServiceDesc{
- ServiceName: "etcdserverpb.Auth",
- HandlerType: (*AuthServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "AuthEnable",
- Handler: _Auth_AuthEnable_Handler,
- },
- {
- MethodName: "AuthDisable",
- Handler: _Auth_AuthDisable_Handler,
- },
- {
- MethodName: "AuthStatus",
- Handler: _Auth_AuthStatus_Handler,
- },
- {
- MethodName: "Authenticate",
- Handler: _Auth_Authenticate_Handler,
- },
- {
- MethodName: "UserAdd",
- Handler: _Auth_UserAdd_Handler,
- },
- {
- MethodName: "UserGet",
- Handler: _Auth_UserGet_Handler,
- },
- {
- MethodName: "UserList",
- Handler: _Auth_UserList_Handler,
- },
- {
- MethodName: "UserDelete",
- Handler: _Auth_UserDelete_Handler,
- },
- {
- MethodName: "UserChangePassword",
- Handler: _Auth_UserChangePassword_Handler,
- },
- {
- MethodName: "UserGrantRole",
- Handler: _Auth_UserGrantRole_Handler,
- },
- {
- MethodName: "UserRevokeRole",
- Handler: _Auth_UserRevokeRole_Handler,
- },
- {
- MethodName: "RoleAdd",
- Handler: _Auth_RoleAdd_Handler,
- },
- {
- MethodName: "RoleGet",
- Handler: _Auth_RoleGet_Handler,
- },
- {
- MethodName: "RoleList",
- Handler: _Auth_RoleList_Handler,
- },
- {
- MethodName: "RoleDelete",
- Handler: _Auth_RoleDelete_Handler,
- },
- {
- MethodName: "RoleGrantPermission",
- Handler: _Auth_RoleGrantPermission_Handler,
- },
- {
- MethodName: "RoleRevokePermission",
- Handler: _Auth_RoleRevokePermission_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "rpc.proto",
-}
-
-func (m *ResponseHeader) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *ResponseHeader) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.RaftTerm != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.RaftTerm))
- i--
- dAtA[i] = 0x20
- }
- if m.Revision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
- i--
- dAtA[i] = 0x18
- }
- if m.MemberId != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MemberId))
- i--
- dAtA[i] = 0x10
- }
- if m.ClusterId != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ClusterId))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *RangeRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *RangeRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RangeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.MaxCreateRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MaxCreateRevision))
- i--
- dAtA[i] = 0x68
- }
- if m.MinCreateRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MinCreateRevision))
- i--
- dAtA[i] = 0x60
- }
- if m.MaxModRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MaxModRevision))
- i--
- dAtA[i] = 0x58
- }
- if m.MinModRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MinModRevision))
- i--
- dAtA[i] = 0x50
- }
- if m.CountOnly {
- i--
- if m.CountOnly {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x48
- }
- if m.KeysOnly {
- i--
- if m.KeysOnly {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x40
- }
- if m.Serializable {
- i--
- if m.Serializable {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x38
- }
- if m.SortTarget != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.SortTarget))
- i--
- dAtA[i] = 0x30
- }
- if m.SortOrder != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.SortOrder))
- i--
- dAtA[i] = 0x28
- }
- if m.Revision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
- i--
- dAtA[i] = 0x20
- }
- if m.Limit != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Limit))
- i--
- dAtA[i] = 0x18
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *RangeResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *RangeResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Count != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Count))
- i--
- dAtA[i] = 0x20
- }
- if m.More {
- i--
- if m.More {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x18
- }
- if len(m.Kvs) > 0 {
- for iNdEx := len(m.Kvs) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Kvs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *PutRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *PutRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *PutRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.IgnoreLease {
- i--
- if m.IgnoreLease {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x30
- }
- if m.IgnoreValue {
- i--
- if m.IgnoreValue {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x28
- }
- if m.PrevKv {
- i--
- if m.PrevKv {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x20
- }
- if m.Lease != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x18
- }
- if len(m.Value) > 0 {
- i -= len(m.Value)
- copy(dAtA[i:], m.Value)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Value)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *PutResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *PutResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *PutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.PrevKv != nil {
- {
- size, err := m.PrevKv.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DeleteRangeRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DeleteRangeRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DeleteRangeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.PrevKv {
- i--
- if m.PrevKv {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x18
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DeleteRangeResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DeleteRangeResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DeleteRangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.PrevKvs) > 0 {
- for iNdEx := len(m.PrevKvs) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.PrevKvs[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- }
- if m.Deleted != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Deleted))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *RequestOp) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *RequestOp) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RequestOp) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Request != nil {
- {
- size := m.Request.Size()
- i -= size
- if _, err := m.Request.MarshalTo(dAtA[i:]); err != nil {
- return 0, err
- }
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *RequestOp_RequestRange) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RequestOp_RequestRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.RequestRange != nil {
- {
- size, err := m.RequestRange.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-func (m *RequestOp_RequestPut) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RequestOp_RequestPut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.RequestPut != nil {
- {
- size, err := m.RequestPut.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- return len(dAtA) - i, nil
-}
-func (m *RequestOp_RequestDeleteRange) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RequestOp_RequestDeleteRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.RequestDeleteRange != nil {
- {
- size, err := m.RequestDeleteRange.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- return len(dAtA) - i, nil
-}
-func (m *RequestOp_RequestTxn) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *RequestOp_RequestTxn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.RequestTxn != nil {
- {
- size, err := m.RequestTxn.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x22
- }
- return len(dAtA) - i, nil
-}
-func (m *ResponseOp) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *ResponseOp) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseOp) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Response != nil {
- {
- size := m.Response.Size()
- i -= size
- if _, err := m.Response.MarshalTo(dAtA[i:]); err != nil {
- return 0, err
- }
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *ResponseOp_ResponseRange) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseOp_ResponseRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.ResponseRange != nil {
- {
- size, err := m.ResponseRange.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-func (m *ResponseOp_ResponsePut) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseOp_ResponsePut) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.ResponsePut != nil {
- {
- size, err := m.ResponsePut.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- return len(dAtA) - i, nil
-}
-func (m *ResponseOp_ResponseDeleteRange) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseOp_ResponseDeleteRange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.ResponseDeleteRange != nil {
- {
- size, err := m.ResponseDeleteRange.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- return len(dAtA) - i, nil
-}
-func (m *ResponseOp_ResponseTxn) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResponseOp_ResponseTxn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.ResponseTxn != nil {
- {
- size, err := m.ResponseTxn.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x22
- }
- return len(dAtA) - i, nil
-}
-func (m *Compare) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *Compare) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x4
- i--
- dAtA[i] = 0x82
- }
- if m.TargetUnion != nil {
- {
- size := m.TargetUnion.Size()
- i -= size
- if _, err := m.TargetUnion.MarshalTo(dAtA[i:]); err != nil {
- return 0, err
- }
- }
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0x1a
- }
- if m.Target != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Target))
- i--
- dAtA[i] = 0x10
- }
- if m.Result != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Result))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *Compare_Version) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare_Version) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- i = encodeVarintRpc(dAtA, i, uint64(m.Version))
- i--
- dAtA[i] = 0x20
- return len(dAtA) - i, nil
-}
-func (m *Compare_CreateRevision) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare_CreateRevision) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- i = encodeVarintRpc(dAtA, i, uint64(m.CreateRevision))
- i--
- dAtA[i] = 0x28
- return len(dAtA) - i, nil
-}
-func (m *Compare_ModRevision) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare_ModRevision) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- i = encodeVarintRpc(dAtA, i, uint64(m.ModRevision))
- i--
- dAtA[i] = 0x30
- return len(dAtA) - i, nil
-}
-func (m *Compare_Value) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare_Value) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.Value != nil {
- i -= len(m.Value)
- copy(dAtA[i:], m.Value)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Value)))
- i--
- dAtA[i] = 0x3a
- }
- return len(dAtA) - i, nil
-}
-func (m *Compare_Lease) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Compare_Lease) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- i = encodeVarintRpc(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x40
- return len(dAtA) - i, nil
-}
-func (m *TxnRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *TxnRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *TxnRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Failure) > 0 {
- for iNdEx := len(m.Failure) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Failure[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- }
- if len(m.Success) > 0 {
- for iNdEx := len(m.Success) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Success[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if len(m.Compare) > 0 {
- for iNdEx := len(m.Compare) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Compare[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *TxnResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *TxnResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *TxnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Responses) > 0 {
- for iNdEx := len(m.Responses) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Responses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- }
- if m.Succeeded {
- i--
- if m.Succeeded {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *CompactionRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *CompactionRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *CompactionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Physical {
- i--
- if m.Physical {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if m.Revision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *CompactionResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *CompactionResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *CompactionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *HashRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *HashRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HashRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *HashKVRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *HashKVRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HashKVRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Revision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Revision))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *HashKVResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *HashKVResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HashKVResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.HashRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.HashRevision))
- i--
- dAtA[i] = 0x20
- }
- if m.CompactRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.CompactRevision))
- i--
- dAtA[i] = 0x18
- }
- if m.Hash != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Hash))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *HashResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *HashResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *HashResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Hash != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Hash))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *SnapshotRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *SnapshotRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *SnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *SnapshotResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *SnapshotResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *SnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Version) > 0 {
- i -= len(m.Version)
- copy(dAtA[i:], m.Version)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
- i--
- dAtA[i] = 0x22
- }
- if len(m.Blob) > 0 {
- i -= len(m.Blob)
- copy(dAtA[i:], m.Blob)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Blob)))
- i--
- dAtA[i] = 0x1a
- }
- if m.RemainingBytes != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.RemainingBytes))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *WatchRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *WatchRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.RequestUnion != nil {
- {
- size := m.RequestUnion.Size()
- i -= size
- if _, err := m.RequestUnion.MarshalTo(dAtA[i:]); err != nil {
- return 0, err
- }
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *WatchRequest_CreateRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchRequest_CreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.CreateRequest != nil {
- {
- size, err := m.CreateRequest.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-func (m *WatchRequest_CancelRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchRequest_CancelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.CancelRequest != nil {
- {
- size, err := m.CancelRequest.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- return len(dAtA) - i, nil
-}
-func (m *WatchRequest_ProgressRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchRequest_ProgressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- if m.ProgressRequest != nil {
- {
- size, err := m.ProgressRequest.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- return len(dAtA) - i, nil
-}
-func (m *WatchCreateRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *WatchCreateRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchCreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Fragment {
- i--
- if m.Fragment {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x40
- }
- if m.WatchId != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
- i--
- dAtA[i] = 0x38
- }
- if m.PrevKv {
- i--
- if m.PrevKv {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x30
- }
- if len(m.Filters) > 0 {
- dAtA22 := make([]byte, len(m.Filters)*10)
- var j21 int
- for _, num := range m.Filters {
- for num >= 1<<7 {
- dAtA22[j21] = uint8(uint64(num)&0x7f | 0x80)
- num >>= 7
- j21++
- }
- dAtA22[j21] = uint8(num)
- j21++
- }
- i -= j21
- copy(dAtA[i:], dAtA22[:j21])
- i = encodeVarintRpc(dAtA, i, uint64(j21))
- i--
- dAtA[i] = 0x2a
- }
- if m.ProgressNotify {
- i--
- if m.ProgressNotify {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x20
- }
- if m.StartRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.StartRevision))
- i--
- dAtA[i] = 0x18
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *WatchCancelRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *WatchCancelRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchCancelRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.WatchId != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *WatchProgressRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *WatchProgressRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchProgressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *WatchResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *WatchResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *WatchResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Events) > 0 {
- for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x5a
- }
- }
- if m.Fragment {
- i--
- if m.Fragment {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x38
- }
- if len(m.CancelReason) > 0 {
- i -= len(m.CancelReason)
- copy(dAtA[i:], m.CancelReason)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.CancelReason)))
- i--
- dAtA[i] = 0x32
- }
- if m.CompactRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.CompactRevision))
- i--
- dAtA[i] = 0x28
- }
- if m.Canceled {
- i--
- if m.Canceled {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x20
- }
- if m.Created {
- i--
- if m.Created {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x18
- }
- if m.WatchId != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseGrantRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseGrantRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseGrantRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x10
- }
- if m.TTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseGrantResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseGrantResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseGrantResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Error) > 0 {
- i -= len(m.Error)
- copy(dAtA[i:], m.Error)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Error)))
- i--
- dAtA[i] = 0x22
- }
- if m.TTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
- i--
- dAtA[i] = 0x18
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseRevokeRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseRevokeRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseRevokeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseRevokeResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseRevokeResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseRevokeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseCheckpoint) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseCheckpoint) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseCheckpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Remaining_TTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Remaining_TTL))
- i--
- dAtA[i] = 0x10
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseCheckpointRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseCheckpointRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseCheckpointRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Checkpoints) > 0 {
- for iNdEx := len(m.Checkpoints) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Checkpoints[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseCheckpointResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseCheckpointResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseCheckpointResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseKeepAliveRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseKeepAliveRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseKeepAliveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseKeepAliveResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseKeepAliveResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseKeepAliveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.TTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
- i--
- dAtA[i] = 0x18
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseTimeToLiveRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseTimeToLiveRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseTimeToLiveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Keys {
- i--
- if m.Keys {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseTimeToLiveResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseTimeToLiveResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseTimeToLiveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Keys) > 0 {
- for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Keys[iNdEx])
- copy(dAtA[i:], m.Keys[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Keys[iNdEx])))
- i--
- dAtA[i] = 0x2a
- }
- }
- if m.GrantedTTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.GrantedTTL))
- i--
- dAtA[i] = 0x20
- }
- if m.TTL != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.TTL))
- i--
- dAtA[i] = 0x18
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseLeasesRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseLeasesRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseLeasesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseStatus) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseStatus) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaseLeasesResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaseLeasesResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LeaseLeasesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Leases) > 0 {
- for iNdEx := len(m.Leases) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Leases[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *Member) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *Member) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Member) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.IsLearner {
- i--
- if m.IsLearner {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x28
- }
- if len(m.ClientURLs) > 0 {
- for iNdEx := len(m.ClientURLs) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.ClientURLs[iNdEx])
- copy(dAtA[i:], m.ClientURLs[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.ClientURLs[iNdEx])))
- i--
- dAtA[i] = 0x22
- }
- }
- if len(m.PeerURLs) > 0 {
- for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.PeerURLs[iNdEx])
- copy(dAtA[i:], m.PeerURLs[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
- i--
- dAtA[i] = 0x1a
- }
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0x12
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberAddRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberAddRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.IsLearner {
- i--
- if m.IsLearner {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if len(m.PeerURLs) > 0 {
- for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.PeerURLs[iNdEx])
- copy(dAtA[i:], m.PeerURLs[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
- i--
- dAtA[i] = 0xa
- }
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberAddResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberAddResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Members) > 0 {
- for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- }
- if m.Member != nil {
- {
- size, err := m.Member.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberRemoveRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberRemoveRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberRemoveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberRemoveResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberRemoveResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberRemoveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Members) > 0 {
- for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberUpdateRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberUpdateRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberUpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.PeerURLs) > 0 {
- for iNdEx := len(m.PeerURLs) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.PeerURLs[iNdEx])
- copy(dAtA[i:], m.PeerURLs[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.PeerURLs[iNdEx])))
- i--
- dAtA[i] = 0x12
- }
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberUpdateResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberUpdateResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberUpdateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Members) > 0 {
- for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberListRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberListRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Linearizable {
- i--
- if m.Linearizable {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberListResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberListResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Members) > 0 {
- for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberPromoteRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberPromoteRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberPromoteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MemberPromoteResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MemberPromoteResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MemberPromoteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Members) > 0 {
- for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DefragmentRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DefragmentRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DefragmentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DefragmentResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DefragmentResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DefragmentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MoveLeaderRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MoveLeaderRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MoveLeaderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.TargetID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.TargetID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *MoveLeaderResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *MoveLeaderResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *MoveLeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AlarmRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AlarmRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AlarmRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Alarm != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Alarm))
- i--
- dAtA[i] = 0x18
- }
- if m.MemberID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MemberID))
- i--
- dAtA[i] = 0x10
- }
- if m.Action != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Action))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AlarmMember) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AlarmMember) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AlarmMember) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Alarm != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Alarm))
- i--
- dAtA[i] = 0x10
- }
- if m.MemberID != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.MemberID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AlarmResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AlarmResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AlarmResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Alarms) > 0 {
- for iNdEx := len(m.Alarms) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Alarms[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DowngradeRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DowngradeRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DowngradeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Version) > 0 {
- i -= len(m.Version)
- copy(dAtA[i:], m.Version)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
- i--
- dAtA[i] = 0x12
- }
- if m.Action != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Action))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DowngradeResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DowngradeResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DowngradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Version) > 0 {
- i -= len(m.Version)
- copy(dAtA[i:], m.Version)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DowngradeVersionTestRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DowngradeVersionTestRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DowngradeVersionTestRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Ver) > 0 {
- i -= len(m.Ver)
- copy(dAtA[i:], m.Ver)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Ver)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *StatusRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *StatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *StatusResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *StatusResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *StatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.DowngradeInfo != nil {
- {
- size, err := m.DowngradeInfo.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x6a
- }
- if m.DbSizeQuota != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.DbSizeQuota))
- i--
- dAtA[i] = 0x60
- }
- if len(m.StorageVersion) > 0 {
- i -= len(m.StorageVersion)
- copy(dAtA[i:], m.StorageVersion)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.StorageVersion)))
- i--
- dAtA[i] = 0x5a
- }
- if m.IsLearner {
- i--
- if m.IsLearner {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x50
- }
- if m.DbSizeInUse != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.DbSizeInUse))
- i--
- dAtA[i] = 0x48
- }
- if len(m.Errors) > 0 {
- for iNdEx := len(m.Errors) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Errors[iNdEx])
- copy(dAtA[i:], m.Errors[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Errors[iNdEx])))
- i--
- dAtA[i] = 0x42
- }
- }
- if m.RaftAppliedIndex != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.RaftAppliedIndex))
- i--
- dAtA[i] = 0x38
- }
- if m.RaftTerm != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.RaftTerm))
- i--
- dAtA[i] = 0x30
- }
- if m.RaftIndex != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.RaftIndex))
- i--
- dAtA[i] = 0x28
- }
- if m.Leader != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.Leader))
- i--
- dAtA[i] = 0x20
- }
- if m.DbSize != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.DbSize))
- i--
- dAtA[i] = 0x18
- }
- if len(m.Version) > 0 {
- i -= len(m.Version)
- copy(dAtA[i:], m.Version)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Version)))
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *DowngradeInfo) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *DowngradeInfo) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *DowngradeInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.TargetVersion) > 0 {
- i -= len(m.TargetVersion)
- copy(dAtA[i:], m.TargetVersion)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.TargetVersion)))
- i--
- dAtA[i] = 0x12
- }
- if m.Enabled {
- i--
- if m.Enabled {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthEnableRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthEnableRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthEnableRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthDisableRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthDisableRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthDisableRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthStatusRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthStatusRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthenticateRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthenticateRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthenticateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Password) > 0 {
- i -= len(m.Password)
- copy(dAtA[i:], m.Password)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserAddRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserAddRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.HashedPassword) > 0 {
- i -= len(m.HashedPassword)
- copy(dAtA[i:], m.HashedPassword)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.HashedPassword)))
- i--
- dAtA[i] = 0x22
- }
- if m.Options != nil {
- {
- size, err := m.Options.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x1a
- }
- if len(m.Password) > 0 {
- i -= len(m.Password)
- copy(dAtA[i:], m.Password)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserGetRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserGetRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserGetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserDeleteRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserChangePasswordRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserChangePasswordRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserChangePasswordRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.HashedPassword) > 0 {
- i -= len(m.HashedPassword)
- copy(dAtA[i:], m.HashedPassword)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.HashedPassword)))
- i--
- dAtA[i] = 0x1a
- }
- if len(m.Password) > 0 {
- i -= len(m.Password)
- copy(dAtA[i:], m.Password)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Password)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserGrantRoleRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserGrantRoleRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserGrantRoleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Role) > 0 {
- i -= len(m.Role)
- copy(dAtA[i:], m.Role)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.User) > 0 {
- i -= len(m.User)
- copy(dAtA[i:], m.User)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.User)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserRevokeRoleRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserRevokeRoleRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserRevokeRoleRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Role) > 0 {
- i -= len(m.Role)
- copy(dAtA[i:], m.Role)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleAddRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleAddRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleAddRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleGetRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleGetRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleGetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Role) > 0 {
- i -= len(m.Role)
- copy(dAtA[i:], m.Role)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserListRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserListRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleListRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleListRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleDeleteRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Role) > 0 {
- i -= len(m.Role)
- copy(dAtA[i:], m.Role)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleGrantPermissionRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleGrantPermissionRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleGrantPermissionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Perm != nil {
- {
- size, err := m.Perm.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleRevokePermissionRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleRevokePermissionRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleRevokePermissionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.RangeEnd) > 0 {
- i -= len(m.RangeEnd)
- copy(dAtA[i:], m.RangeEnd)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.RangeEnd)))
- i--
- dAtA[i] = 0x1a
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Role) > 0 {
- i -= len(m.Role)
- copy(dAtA[i:], m.Role)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Role)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthEnableResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthEnableResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthEnableResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthDisableResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthDisableResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthDisableResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthStatusResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthStatusResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.AuthRevision != 0 {
- i = encodeVarintRpc(dAtA, i, uint64(m.AuthRevision))
- i--
- dAtA[i] = 0x18
- }
- if m.Enabled {
- i--
- if m.Enabled {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthenticateResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthenticateResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthenticateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Token) > 0 {
- i -= len(m.Token)
- copy(dAtA[i:], m.Token)
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Token)))
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserAddResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserAddResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserGetResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserGetResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserGetResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Roles) > 0 {
- for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Roles[iNdEx])
- copy(dAtA[i:], m.Roles[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Roles[iNdEx])))
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserDeleteResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserDeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserChangePasswordResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserChangePasswordResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserChangePasswordResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserGrantRoleResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserGrantRoleResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserGrantRoleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserRevokeRoleResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserRevokeRoleResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserRevokeRoleResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleAddResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleAddResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleAddResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleGetResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleGetResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleGetResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Perm) > 0 {
- for iNdEx := len(m.Perm) - 1; iNdEx >= 0; iNdEx-- {
- {
- size, err := m.Perm[iNdEx].MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleListResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleListResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Roles) > 0 {
- for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Roles[iNdEx])
- copy(dAtA[i:], m.Roles[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Roles[iNdEx])))
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthUserListResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthUserListResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthUserListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Users) > 0 {
- for iNdEx := len(m.Users) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.Users[iNdEx])
- copy(dAtA[i:], m.Users[iNdEx])
- i = encodeVarintRpc(dAtA, i, uint64(len(m.Users[iNdEx])))
- i--
- dAtA[i] = 0x12
- }
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleDeleteResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleDeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleGrantPermissionResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleGrantPermissionResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleGrantPermissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *AuthRoleRevokePermissionResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *AuthRoleRevokePermissionResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *AuthRoleRevokePermissionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRpc(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func encodeVarintRpc(dAtA []byte, offset int, v uint64) int {
- offset -= sovRpc(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *ResponseHeader) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ClusterId != 0 {
- n += 1 + sovRpc(uint64(m.ClusterId))
- }
- if m.MemberId != 0 {
- n += 1 + sovRpc(uint64(m.MemberId))
- }
- if m.Revision != 0 {
- n += 1 + sovRpc(uint64(m.Revision))
- }
- if m.RaftTerm != 0 {
- n += 1 + sovRpc(uint64(m.RaftTerm))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *RangeRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Limit != 0 {
- n += 1 + sovRpc(uint64(m.Limit))
- }
- if m.Revision != 0 {
- n += 1 + sovRpc(uint64(m.Revision))
- }
- if m.SortOrder != 0 {
- n += 1 + sovRpc(uint64(m.SortOrder))
- }
- if m.SortTarget != 0 {
- n += 1 + sovRpc(uint64(m.SortTarget))
- }
- if m.Serializable {
- n += 2
- }
- if m.KeysOnly {
- n += 2
- }
- if m.CountOnly {
- n += 2
- }
- if m.MinModRevision != 0 {
- n += 1 + sovRpc(uint64(m.MinModRevision))
- }
- if m.MaxModRevision != 0 {
- n += 1 + sovRpc(uint64(m.MaxModRevision))
- }
- if m.MinCreateRevision != 0 {
- n += 1 + sovRpc(uint64(m.MinCreateRevision))
- }
- if m.MaxCreateRevision != 0 {
- n += 1 + sovRpc(uint64(m.MaxCreateRevision))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *RangeResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Kvs) > 0 {
- for _, e := range m.Kvs {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.More {
- n += 2
- }
- if m.Count != 0 {
- n += 1 + sovRpc(uint64(m.Count))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *PutRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Value)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Lease != 0 {
- n += 1 + sovRpc(uint64(m.Lease))
- }
- if m.PrevKv {
- n += 2
- }
- if m.IgnoreValue {
- n += 2
- }
- if m.IgnoreLease {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *PutResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.PrevKv != nil {
- l = m.PrevKv.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DeleteRangeRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.PrevKv {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DeleteRangeResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Deleted != 0 {
- n += 1 + sovRpc(uint64(m.Deleted))
- }
- if len(m.PrevKvs) > 0 {
- for _, e := range m.PrevKvs {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *RequestOp) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Request != nil {
- n += m.Request.Size()
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *RequestOp_RequestRange) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.RequestRange != nil {
- l = m.RequestRange.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *RequestOp_RequestPut) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.RequestPut != nil {
- l = m.RequestPut.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *RequestOp_RequestDeleteRange) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.RequestDeleteRange != nil {
- l = m.RequestDeleteRange.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *RequestOp_RequestTxn) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.RequestTxn != nil {
- l = m.RequestTxn.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *ResponseOp) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Response != nil {
- n += m.Response.Size()
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *ResponseOp_ResponseRange) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ResponseRange != nil {
- l = m.ResponseRange.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *ResponseOp_ResponsePut) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ResponsePut != nil {
- l = m.ResponsePut.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *ResponseOp_ResponseDeleteRange) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ResponseDeleteRange != nil {
- l = m.ResponseDeleteRange.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *ResponseOp_ResponseTxn) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ResponseTxn != nil {
- l = m.ResponseTxn.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *Compare) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Result != 0 {
- n += 1 + sovRpc(uint64(m.Result))
- }
- if m.Target != 0 {
- n += 1 + sovRpc(uint64(m.Target))
- }
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.TargetUnion != nil {
- n += m.TargetUnion.Size()
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 2 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *Compare_Version) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRpc(uint64(m.Version))
- return n
-}
-func (m *Compare_CreateRevision) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRpc(uint64(m.CreateRevision))
- return n
-}
-func (m *Compare_ModRevision) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRpc(uint64(m.ModRevision))
- return n
-}
-func (m *Compare_Value) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Value != nil {
- l = len(m.Value)
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *Compare_Lease) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRpc(uint64(m.Lease))
- return n
-}
-func (m *TxnRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if len(m.Compare) > 0 {
- for _, e := range m.Compare {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if len(m.Success) > 0 {
- for _, e := range m.Success {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if len(m.Failure) > 0 {
- for _, e := range m.Failure {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *TxnResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Succeeded {
- n += 2
- }
- if len(m.Responses) > 0 {
- for _, e := range m.Responses {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *CompactionRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Revision != 0 {
- n += 1 + sovRpc(uint64(m.Revision))
- }
- if m.Physical {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *CompactionResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *HashRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *HashKVRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Revision != 0 {
- n += 1 + sovRpc(uint64(m.Revision))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *HashKVResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Hash != 0 {
- n += 1 + sovRpc(uint64(m.Hash))
- }
- if m.CompactRevision != 0 {
- n += 1 + sovRpc(uint64(m.CompactRevision))
- }
- if m.HashRevision != 0 {
- n += 1 + sovRpc(uint64(m.HashRevision))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *HashResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Hash != 0 {
- n += 1 + sovRpc(uint64(m.Hash))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *SnapshotRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *SnapshotResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.RemainingBytes != 0 {
- n += 1 + sovRpc(uint64(m.RemainingBytes))
- }
- l = len(m.Blob)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Version)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *WatchRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.RequestUnion != nil {
- n += m.RequestUnion.Size()
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *WatchRequest_CreateRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.CreateRequest != nil {
- l = m.CreateRequest.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *WatchRequest_CancelRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.CancelRequest != nil {
- l = m.CancelRequest.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *WatchRequest_ProgressRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ProgressRequest != nil {
- l = m.ProgressRequest.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- return n
-}
-func (m *WatchCreateRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.StartRevision != 0 {
- n += 1 + sovRpc(uint64(m.StartRevision))
- }
- if m.ProgressNotify {
- n += 2
- }
- if len(m.Filters) > 0 {
- l = 0
- for _, e := range m.Filters {
- l += sovRpc(uint64(e))
- }
- n += 1 + sovRpc(uint64(l)) + l
- }
- if m.PrevKv {
- n += 2
- }
- if m.WatchId != 0 {
- n += 1 + sovRpc(uint64(m.WatchId))
- }
- if m.Fragment {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *WatchCancelRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.WatchId != 0 {
- n += 1 + sovRpc(uint64(m.WatchId))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *WatchProgressRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *WatchResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.WatchId != 0 {
- n += 1 + sovRpc(uint64(m.WatchId))
- }
- if m.Created {
- n += 2
- }
- if m.Canceled {
- n += 2
- }
- if m.CompactRevision != 0 {
- n += 1 + sovRpc(uint64(m.CompactRevision))
- }
- l = len(m.CancelReason)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Fragment {
- n += 2
- }
- if len(m.Events) > 0 {
- for _, e := range m.Events {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseGrantRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.TTL != 0 {
- n += 1 + sovRpc(uint64(m.TTL))
- }
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseGrantResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.TTL != 0 {
- n += 1 + sovRpc(uint64(m.TTL))
- }
- l = len(m.Error)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseRevokeRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseRevokeResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseCheckpoint) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.Remaining_TTL != 0 {
- n += 1 + sovRpc(uint64(m.Remaining_TTL))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseCheckpointRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if len(m.Checkpoints) > 0 {
- for _, e := range m.Checkpoints {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseCheckpointResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseKeepAliveRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseKeepAliveResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.TTL != 0 {
- n += 1 + sovRpc(uint64(m.TTL))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseTimeToLiveRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.Keys {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseTimeToLiveResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.TTL != 0 {
- n += 1 + sovRpc(uint64(m.TTL))
- }
- if m.GrantedTTL != 0 {
- n += 1 + sovRpc(uint64(m.GrantedTTL))
- }
- if len(m.Keys) > 0 {
- for _, b := range m.Keys {
- l = len(b)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseLeasesRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseStatus) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaseLeasesResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Leases) > 0 {
- for _, e := range m.Leases {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *Member) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.PeerURLs) > 0 {
- for _, s := range m.PeerURLs {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if len(m.ClientURLs) > 0 {
- for _, s := range m.ClientURLs {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.IsLearner {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberAddRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if len(m.PeerURLs) > 0 {
- for _, s := range m.PeerURLs {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.IsLearner {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberAddResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Member != nil {
- l = m.Member.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Members) > 0 {
- for _, e := range m.Members {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberRemoveRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberRemoveResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Members) > 0 {
- for _, e := range m.Members {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberUpdateRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if len(m.PeerURLs) > 0 {
- for _, s := range m.PeerURLs {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberUpdateResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Members) > 0 {
- for _, e := range m.Members {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberListRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Linearizable {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberListResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Members) > 0 {
- for _, e := range m.Members {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberPromoteRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovRpc(uint64(m.ID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MemberPromoteResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Members) > 0 {
- for _, e := range m.Members {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DefragmentRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DefragmentResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MoveLeaderRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.TargetID != 0 {
- n += 1 + sovRpc(uint64(m.TargetID))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *MoveLeaderResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AlarmRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Action != 0 {
- n += 1 + sovRpc(uint64(m.Action))
- }
- if m.MemberID != 0 {
- n += 1 + sovRpc(uint64(m.MemberID))
- }
- if m.Alarm != 0 {
- n += 1 + sovRpc(uint64(m.Alarm))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AlarmMember) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.MemberID != 0 {
- n += 1 + sovRpc(uint64(m.MemberID))
- }
- if m.Alarm != 0 {
- n += 1 + sovRpc(uint64(m.Alarm))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AlarmResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Alarms) > 0 {
- for _, e := range m.Alarms {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DowngradeRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Action != 0 {
- n += 1 + sovRpc(uint64(m.Action))
- }
- l = len(m.Version)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DowngradeResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Version)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DowngradeVersionTestRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Ver)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *StatusRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *StatusResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Version)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.DbSize != 0 {
- n += 1 + sovRpc(uint64(m.DbSize))
- }
- if m.Leader != 0 {
- n += 1 + sovRpc(uint64(m.Leader))
- }
- if m.RaftIndex != 0 {
- n += 1 + sovRpc(uint64(m.RaftIndex))
- }
- if m.RaftTerm != 0 {
- n += 1 + sovRpc(uint64(m.RaftTerm))
- }
- if m.RaftAppliedIndex != 0 {
- n += 1 + sovRpc(uint64(m.RaftAppliedIndex))
- }
- if len(m.Errors) > 0 {
- for _, s := range m.Errors {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.DbSizeInUse != 0 {
- n += 1 + sovRpc(uint64(m.DbSizeInUse))
- }
- if m.IsLearner {
- n += 2
- }
- l = len(m.StorageVersion)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.DbSizeQuota != 0 {
- n += 1 + sovRpc(uint64(m.DbSizeQuota))
- }
- if m.DowngradeInfo != nil {
- l = m.DowngradeInfo.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *DowngradeInfo) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Enabled {
- n += 2
- }
- l = len(m.TargetVersion)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthEnableRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthDisableRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthStatusRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthenticateRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Password)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserAddRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Password)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Options != nil {
- l = m.Options.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.HashedPassword)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserGetRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserDeleteRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserChangePasswordRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Password)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.HashedPassword)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserGrantRoleRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.User)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Role)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserRevokeRoleRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Role)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleAddRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleGetRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Role)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserListRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleListRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleDeleteRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Role)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleGrantPermissionRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Perm != nil {
- l = m.Perm.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleRevokePermissionRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Role)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.RangeEnd)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthEnableResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthDisableResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthStatusResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.Enabled {
- n += 2
- }
- if m.AuthRevision != 0 {
- n += 1 + sovRpc(uint64(m.AuthRevision))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthenticateResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- l = len(m.Token)
- if l > 0 {
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserAddResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserGetResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Roles) > 0 {
- for _, s := range m.Roles {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserDeleteResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserChangePasswordResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserGrantRoleResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserRevokeRoleResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleAddResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleGetResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Perm) > 0 {
- for _, e := range m.Perm {
- l = e.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleListResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Roles) > 0 {
- for _, s := range m.Roles {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthUserListResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if len(m.Users) > 0 {
- for _, s := range m.Users {
- l = len(s)
- n += 1 + l + sovRpc(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleDeleteResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleGrantPermissionResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *AuthRoleRevokePermissionResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovRpc(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func sovRpc(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozRpc(x uint64) (n int) {
- return sovRpc(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *ResponseHeader) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ResponseHeader: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ResponseHeader: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType)
- }
- m.ClusterId = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ClusterId |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType)
- }
- m.MemberId = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MemberId |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
- }
- m.Revision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Revision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType)
- }
- m.RaftTerm = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RaftTerm |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *RangeRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: RangeRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: RangeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType)
- }
- m.Limit = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Limit |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
- }
- m.Revision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Revision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field SortOrder", wireType)
- }
- m.SortOrder = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.SortOrder |= RangeRequest_SortOrder(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field SortTarget", wireType)
- }
- m.SortTarget = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.SortTarget |= RangeRequest_SortTarget(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 7:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Serializable", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Serializable = bool(v != 0)
- case 8:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field KeysOnly", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.KeysOnly = bool(v != 0)
- case 9:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CountOnly", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.CountOnly = bool(v != 0)
- case 10:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MinModRevision", wireType)
- }
- m.MinModRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MinModRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 11:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MaxModRevision", wireType)
- }
- m.MaxModRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MaxModRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 12:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MinCreateRevision", wireType)
- }
- m.MinCreateRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MinCreateRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 13:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MaxCreateRevision", wireType)
- }
- m.MaxCreateRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MaxCreateRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *RangeResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: RangeResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: RangeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Kvs", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Kvs = append(m.Kvs, &mvccpb.KeyValue{})
- if err := m.Kvs[len(m.Kvs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field More", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.More = bool(v != 0)
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType)
- }
- m.Count = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Count |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *PutRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: PutRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: PutRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
- if m.Value == nil {
- m.Value = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- m.Lease = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Lease |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.PrevKv = bool(v != 0)
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IgnoreValue", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IgnoreValue = bool(v != 0)
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IgnoreLease", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IgnoreLease = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *PutResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: PutResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: PutResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.PrevKv == nil {
- m.PrevKv = &mvccpb.KeyValue{}
- }
- if err := m.PrevKv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DeleteRangeRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DeleteRangeRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DeleteRangeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.PrevKv = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DeleteRangeResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DeleteRangeResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DeleteRangeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType)
- }
- m.Deleted = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Deleted |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKvs", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PrevKvs = append(m.PrevKvs, &mvccpb.KeyValue{})
- if err := m.PrevKvs[len(m.PrevKvs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *RequestOp) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: RequestOp: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: RequestOp: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RequestRange", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &RangeRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Request = &RequestOp_RequestRange{v}
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RequestPut", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &PutRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Request = &RequestOp_RequestPut{v}
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RequestDeleteRange", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &DeleteRangeRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Request = &RequestOp_RequestDeleteRange{v}
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RequestTxn", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &TxnRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Request = &RequestOp_RequestTxn{v}
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *ResponseOp) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ResponseOp: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ResponseOp: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ResponseRange", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &RangeResponse{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Response = &ResponseOp_ResponseRange{v}
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ResponsePut", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &PutResponse{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Response = &ResponseOp_ResponsePut{v}
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ResponseDeleteRange", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &DeleteRangeResponse{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Response = &ResponseOp_ResponseDeleteRange{v}
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ResponseTxn", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &TxnResponse{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.Response = &ResponseOp_ResponseTxn{v}
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *Compare) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Compare: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Compare: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType)
- }
- m.Result = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Result |= Compare_CompareResult(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType)
- }
- m.Target = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Target |= Compare_CompareTarget(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- var v int64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.TargetUnion = &Compare_Version{v}
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
- }
- var v int64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.TargetUnion = &Compare_CreateRevision{v}
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
- }
- var v int64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.TargetUnion = &Compare_ModRevision{v}
- case 7:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := make([]byte, postIndex-iNdEx)
- copy(v, dAtA[iNdEx:postIndex])
- m.TargetUnion = &Compare_Value{v}
- iNdEx = postIndex
- case 8:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- var v int64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.TargetUnion = &Compare_Lease{v}
- case 64:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *TxnRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: TxnRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: TxnRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Compare", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Compare = append(m.Compare, &Compare{})
- if err := m.Compare[len(m.Compare)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Success = append(m.Success, &RequestOp{})
- if err := m.Success[len(m.Success)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Failure", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Failure = append(m.Failure, &RequestOp{})
- if err := m.Failure[len(m.Failure)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *TxnResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: TxnResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: TxnResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Succeeded", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Succeeded = bool(v != 0)
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Responses", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Responses = append(m.Responses, &ResponseOp{})
- if err := m.Responses[len(m.Responses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *CompactionRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: CompactionRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: CompactionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
- }
- m.Revision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Revision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Physical", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Physical = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *CompactionResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: CompactionResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: CompactionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *HashRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: HashRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: HashRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *HashKVRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: HashKVRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: HashKVRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
- }
- m.Revision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Revision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *HashKVResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: HashKVResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: HashKVResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
- }
- m.Hash = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Hash |= uint32(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CompactRevision", wireType)
- }
- m.CompactRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.CompactRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field HashRevision", wireType)
- }
- m.HashRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.HashRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *HashResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: HashResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: HashResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
- }
- m.Hash = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Hash |= uint32(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *SnapshotRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: SnapshotRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: SnapshotRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *SnapshotResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: SnapshotResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: SnapshotResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RemainingBytes", wireType)
- }
- m.RemainingBytes = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RemainingBytes |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Blob", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Blob = append(m.Blob[:0], dAtA[iNdEx:postIndex]...)
- if m.Blob == nil {
- m.Blob = []byte{}
- }
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Version = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *WatchRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: WatchRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: WatchRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field CreateRequest", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &WatchCreateRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.RequestUnion = &WatchRequest_CreateRequest{v}
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field CancelRequest", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &WatchCancelRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.RequestUnion = &WatchRequest_CancelRequest{v}
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ProgressRequest", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- v := &WatchProgressRequest{}
- if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- m.RequestUnion = &WatchRequest_ProgressRequest{v}
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *WatchCreateRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: WatchCreateRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: WatchCreateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field StartRevision", wireType)
- }
- m.StartRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.StartRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ProgressNotify", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.ProgressNotify = bool(v != 0)
- case 5:
- if wireType == 0 {
- var v WatchCreateRequest_FilterType
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= WatchCreateRequest_FilterType(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Filters = append(m.Filters, v)
- } else if wireType == 2 {
- var packedLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- packedLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if packedLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + packedLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- var elementCount int
- if elementCount != 0 && len(m.Filters) == 0 {
- m.Filters = make([]WatchCreateRequest_FilterType, 0, elementCount)
- }
- for iNdEx < postIndex {
- var v WatchCreateRequest_FilterType
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= WatchCreateRequest_FilterType(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Filters = append(m.Filters, v)
- }
- } else {
- return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType)
- }
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.PrevKv = bool(v != 0)
- case 7:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
- }
- m.WatchId = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.WatchId |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 8:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Fragment", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Fragment = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *WatchCancelRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: WatchCancelRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: WatchCancelRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
- }
- m.WatchId = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.WatchId |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *WatchProgressRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: WatchProgressRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: WatchProgressRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *WatchResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: WatchResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: WatchResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
- }
- m.WatchId = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.WatchId |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Created", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Created = bool(v != 0)
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Canceled", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Canceled = bool(v != 0)
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CompactRevision", wireType)
- }
- m.CompactRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.CompactRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 6:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field CancelReason", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.CancelReason = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 7:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Fragment", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Fragment = bool(v != 0)
- case 11:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Events = append(m.Events, &mvccpb.Event{})
- if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseGrantRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseGrantRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseGrantRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
- }
- m.TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseGrantResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseGrantResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseGrantResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
- }
- m.TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Error = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseRevokeRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseRevokeRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseRevokeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseRevokeResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseRevokeResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseRevokeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseCheckpoint) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseCheckpoint: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseCheckpoint: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Remaining_TTL", wireType)
- }
- m.Remaining_TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Remaining_TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseCheckpointRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseCheckpointRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseCheckpointRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Checkpoints", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Checkpoints = append(m.Checkpoints, &LeaseCheckpoint{})
- if err := m.Checkpoints[len(m.Checkpoints)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseCheckpointResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseCheckpointResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseCheckpointResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseKeepAliveRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseKeepAliveRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseKeepAliveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseKeepAliveResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseKeepAliveResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseKeepAliveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
- }
- m.TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseTimeToLiveRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseTimeToLiveRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseTimeToLiveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Keys = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseTimeToLiveResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseTimeToLiveResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseTimeToLiveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
- }
- m.TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field GrantedTTL", wireType)
- }
- m.GrantedTTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.GrantedTTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 5:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Keys = append(m.Keys, make([]byte, postIndex-iNdEx))
- copy(m.Keys[len(m.Keys)-1], dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseLeasesRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseLeasesRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseLeasesRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseStatus) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseStatus: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseStatus: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *LeaseLeasesResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseLeasesResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseLeasesResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Leases", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Leases = append(m.Leases, &LeaseStatus{})
- if err := m.Leases[len(m.Leases)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *Member) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Member: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Member: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClientURLs", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.ClientURLs = append(m.ClientURLs, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IsLearner = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberAddRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberAddRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IsLearner = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberAddResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberAddResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Member", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Member == nil {
- m.Member = &Member{}
- }
- if err := m.Member.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Members = append(m.Members, &Member{})
- if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberRemoveRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberRemoveRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberRemoveRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberRemoveResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberRemoveResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberRemoveResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Members = append(m.Members, &Member{})
- if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberUpdateRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberUpdateRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberUpdateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PeerURLs", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PeerURLs = append(m.PeerURLs, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberUpdateResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberUpdateResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberUpdateResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Members = append(m.Members, &Member{})
- if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberListRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberListRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Linearizable", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Linearizable = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberListResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberListResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Members = append(m.Members, &Member{})
- if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberPromoteRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberPromoteRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberPromoteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MemberPromoteResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MemberPromoteResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MemberPromoteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Members = append(m.Members, &Member{})
- if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DefragmentRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DefragmentRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DefragmentRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DefragmentResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DefragmentResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DefragmentResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MoveLeaderRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MoveLeaderRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MoveLeaderRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TargetID", wireType)
- }
- m.TargetID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TargetID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *MoveLeaderResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: MoveLeaderResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: MoveLeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AlarmRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AlarmRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AlarmRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
- }
- m.Action = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Action |= AlarmRequest_AlarmAction(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MemberID", wireType)
- }
- m.MemberID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MemberID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
- }
- m.Alarm = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Alarm |= AlarmType(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AlarmMember) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AlarmMember: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AlarmMember: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field MemberID", wireType)
- }
- m.MemberID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.MemberID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType)
- }
- m.Alarm = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Alarm |= AlarmType(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AlarmResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AlarmResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AlarmResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Alarms", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Alarms = append(m.Alarms, &AlarmMember{})
- if err := m.Alarms[len(m.Alarms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DowngradeRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DowngradeRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DowngradeRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
- }
- m.Action = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Action |= DowngradeRequest_DowngradeAction(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Version = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DowngradeResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DowngradeResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DowngradeResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Version = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DowngradeVersionTestRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DowngradeVersionTestRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DowngradeVersionTestRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Ver = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *StatusRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: StatusRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: StatusRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *StatusResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: StatusResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: StatusResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Version = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field DbSize", wireType)
- }
- m.DbSize = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.DbSize |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType)
- }
- m.Leader = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Leader |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 5:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RaftIndex", wireType)
- }
- m.RaftIndex = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RaftIndex |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType)
- }
- m.RaftTerm = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RaftTerm |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 7:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RaftAppliedIndex", wireType)
- }
- m.RaftAppliedIndex = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RaftAppliedIndex |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 8:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Errors", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Errors = append(m.Errors, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 9:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field DbSizeInUse", wireType)
- }
- m.DbSizeInUse = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.DbSizeInUse |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 10:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IsLearner = bool(v != 0)
- case 11:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field StorageVersion", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.StorageVersion = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 12:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field DbSizeQuota", wireType)
- }
- m.DbSizeQuota = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.DbSizeQuota |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 13:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field DowngradeInfo", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.DowngradeInfo == nil {
- m.DowngradeInfo = &DowngradeInfo{}
- }
- if err := m.DowngradeInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *DowngradeInfo) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DowngradeInfo: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DowngradeInfo: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Enabled = bool(v != 0)
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field TargetVersion", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.TargetVersion = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthEnableRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthEnableRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthEnableRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthDisableRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthDisableRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthDisableRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthStatusRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthStatusRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthenticateRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthenticateRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthenticateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Password = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserAddRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserAddRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Password = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Options == nil {
- m.Options = &authpb.UserAddOptions{}
- }
- if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 4:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field HashedPassword", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.HashedPassword = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserGetRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserGetRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserGetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserDeleteRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserDeleteRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserChangePasswordRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserChangePasswordRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserChangePasswordRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Password = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field HashedPassword", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.HashedPassword = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserGrantRoleRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserGrantRoleRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserGrantRoleRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field User", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.User = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Role = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserRevokeRoleRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserRevokeRoleRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserRevokeRoleRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Role = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleAddRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleAddRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleAddRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleGetRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleGetRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleGetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Role = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserListRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserListRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleListRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleListRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleListRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleDeleteRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleDeleteRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Role = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleGrantPermissionRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleGrantPermissionRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleGrantPermissionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Perm", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Perm == nil {
- m.Perm = &authpb.Permission{}
- }
- if err := m.Perm.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleRevokePermissionRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleRevokePermissionRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleRevokePermissionRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Role = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.RangeEnd = append(m.RangeEnd[:0], dAtA[iNdEx:postIndex]...)
- if m.RangeEnd == nil {
- m.RangeEnd = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthEnableResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthEnableResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthEnableResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthDisableResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthDisableResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthDisableResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthStatusResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthStatusResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Enabled = bool(v != 0)
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field AuthRevision", wireType)
- }
- m.AuthRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.AuthRevision |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthenticateResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthenticateResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthenticateResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Token = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserAddResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserAddResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserGetResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserGetResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserGetResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserDeleteResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserDeleteResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserChangePasswordResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserChangePasswordResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserChangePasswordResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserGrantRoleResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserGrantRoleResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserGrantRoleResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserRevokeRoleResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserRevokeRoleResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserRevokeRoleResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleAddResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleAddResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleAddResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleGetResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleGetResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleGetResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Perm", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Perm = append(m.Perm, &authpb.Permission{})
- if err := m.Perm[len(m.Perm)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleListResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleListResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthUserListResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthUserListResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthUserListResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Users", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Users = append(m.Users, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleDeleteResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleDeleteResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleGrantPermissionResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleGrantPermissionResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleGrantPermissionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *AuthRoleRevokePermissionResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: AuthRoleRevokePermissionResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: AuthRoleRevokePermissionResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRpc
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRpc
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRpc(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRpc
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func skipRpc(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRpc
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthRpc
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupRpc
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthRpc
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
-
-var (
- ErrInvalidLengthRpc = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowRpc = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupRpc = fmt.Errorf("proto: unexpected end of group")
-)
diff --git a/api/etcdserverpb/rpc.proto b/api/etcdserverpb/rpc.proto
index 983dc01725f4..31d5283473b0 100644
--- a/api/etcdserverpb/rpc.proto
+++ b/api/etcdserverpb/rpc.proto
@@ -1,7 +1,6 @@
syntax = "proto3";
package etcdserverpb;
-import "gogoproto/gogo.proto";
import "etcd/api/mvccpb/kv.proto";
import "etcd/api/authpb/auth.proto";
import "etcd/api/versionpb/version.proto";
@@ -12,9 +11,6 @@ import "protoc-gen-openapiv2/options/annotations.proto";
option go_package = "go.etcd.io/etcd/api/v3/etcdserverpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
security_definitions: {
security: {
@@ -43,6 +39,14 @@ service KV {
};
}
+ // RangeStream gets the keys in the range from the key-value store.
+ //
+ // This RPC is intentionally gRPC-only and does not provide a
+ // grpc-gateway REST mapping, because streaming chunked responses
+ // are not a good fit for standard JSON/REST semantics.
+ rpc RangeStream(RangeRequest) returns (stream RangeStreamResponse) {
+ }
+
// Put puts the given key into the key-value store.
// A put request increments the revision of the key-value store
// and generates one event in the event history.
@@ -345,7 +349,7 @@ service Auth {
};
}
- // UserGrant grants a role to a specified user.
+ // UserGrantRole grants a role to a specified user.
rpc UserGrantRole(AuthUserGrantRoleRequest) returns (AuthUserGrantRoleResponse) {
option (google.api.http) = {
post: "/v3/auth/user/grant"
@@ -1434,3 +1438,20 @@ message AuthRoleRevokePermissionResponse {
ResponseHeader header = 1;
}
+
+// RangeStreamResponse is the response for the RangeStream RPC.
+message RangeStreamResponse {
+ option (versionpb.etcd_version_msg) = "3.7";
+ // range_response is a partial response for the KV.RangeStream RPC.
+ // The result of proto.Merge() applied on all responses should result in the
+ // same RangeResponse response as to the Range() method.
+ //
+ // Field population across chunks:
+ // - kvs: each chunk carries a disjoint slice of the result. Concatenating
+ // them in order yields the full key set.
+ // - header, more, count: set only on the final chunk. Non-merge clients
+ // can rely on io.EOF (end of stream) to detect completion. These
+ // fields are provided for clients that merge all responses into a
+ // single RangeResponse.
+ RangeResponse range_response = 1;
+}
diff --git a/api/etcdserverpb/rpc_grpc.pb.go b/api/etcdserverpb/rpc_grpc.pb.go
new file mode 100644
index 000000000000..843632e19111
--- /dev/null
+++ b/api/etcdserverpb/rpc_grpc.pb.go
@@ -0,0 +1,2137 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.6.2
+// - protoc v3.20.3
+// source: rpc.proto
+
+package etcdserverpb
+
+import (
+ context "context"
+
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ KV_Range_FullMethodName = "/etcdserverpb.KV/Range"
+ KV_RangeStream_FullMethodName = "/etcdserverpb.KV/RangeStream"
+ KV_Put_FullMethodName = "/etcdserverpb.KV/Put"
+ KV_DeleteRange_FullMethodName = "/etcdserverpb.KV/DeleteRange"
+ KV_Txn_FullMethodName = "/etcdserverpb.KV/Txn"
+ KV_Compact_FullMethodName = "/etcdserverpb.KV/Compact"
+)
+
+// KVClient is the client API for KV service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type KVClient interface {
+ // Range gets the keys in the range from the key-value store.
+ Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error)
+ // RangeStream gets the keys in the range from the key-value store.
+ //
+ // This RPC is intentionally gRPC-only and does not provide a
+ // grpc-gateway REST mapping, because streaming chunked responses
+ // are not a good fit for standard JSON/REST semantics.
+ RangeStream(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RangeStreamResponse], error)
+ // Put puts the given key into the key-value store.
+ // A put request increments the revision of the key-value store
+ // and generates one event in the event history.
+ Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error)
+ // DeleteRange deletes the given range from the key-value store.
+ // A delete request increments the revision of the key-value store
+ // and generates a delete event in the event history for every deleted key.
+ DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error)
+ // Txn processes multiple requests in a single transaction.
+ // A txn request increments the revision of the key-value store
+ // and generates events with the same revision for every completed request.
+ // It is not allowed to modify the same key several times within one txn.
+ Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error)
+ // Compact compacts the event history in the etcd key-value store. The key-value
+ // store should be periodically compacted or the event history will continue to grow
+ // indefinitely.
+ Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error)
+}
+
+type kVClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewKVClient(cc grpc.ClientConnInterface) KVClient {
+ return &kVClient{cc}
+}
+
+func (c *kVClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(RangeResponse)
+ err := c.cc.Invoke(ctx, KV_Range_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *kVClient) RangeStream(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[RangeStreamResponse], error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &KV_ServiceDesc.Streams[0], KV_RangeStream_FullMethodName, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &grpc.GenericClientStream[RangeRequest, RangeStreamResponse]{ClientStream: stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type KV_RangeStreamClient = grpc.ServerStreamingClient[RangeStreamResponse]
+
+func (c *kVClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(PutResponse)
+ err := c.cc.Invoke(ctx, KV_Put_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *kVClient) DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(DeleteRangeResponse)
+ err := c.cc.Invoke(ctx, KV_DeleteRange_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *kVClient) Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(TxnResponse)
+ err := c.cc.Invoke(ctx, KV_Txn_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *kVClient) Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(CompactionResponse)
+ err := c.cc.Invoke(ctx, KV_Compact_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// KVServer is the server API for KV service.
+// All implementations must embed UnimplementedKVServer
+// for forward compatibility.
+type KVServer interface {
+ // Range gets the keys in the range from the key-value store.
+ Range(context.Context, *RangeRequest) (*RangeResponse, error)
+ // RangeStream gets the keys in the range from the key-value store.
+ //
+ // This RPC is intentionally gRPC-only and does not provide a
+ // grpc-gateway REST mapping, because streaming chunked responses
+ // are not a good fit for standard JSON/REST semantics.
+ RangeStream(*RangeRequest, grpc.ServerStreamingServer[RangeStreamResponse]) error
+ // Put puts the given key into the key-value store.
+ // A put request increments the revision of the key-value store
+ // and generates one event in the event history.
+ Put(context.Context, *PutRequest) (*PutResponse, error)
+ // DeleteRange deletes the given range from the key-value store.
+ // A delete request increments the revision of the key-value store
+ // and generates a delete event in the event history for every deleted key.
+ DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error)
+ // Txn processes multiple requests in a single transaction.
+ // A txn request increments the revision of the key-value store
+ // and generates events with the same revision for every completed request.
+ // It is not allowed to modify the same key several times within one txn.
+ Txn(context.Context, *TxnRequest) (*TxnResponse, error)
+ // Compact compacts the event history in the etcd key-value store. The key-value
+ // store should be periodically compacted or the event history will continue to grow
+ // indefinitely.
+ Compact(context.Context, *CompactionRequest) (*CompactionResponse, error)
+ mustEmbedUnimplementedKVServer()
+}
+
+// UnimplementedKVServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedKVServer struct{}
+
+func (UnimplementedKVServer) Range(context.Context, *RangeRequest) (*RangeResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Range not implemented")
+}
+func (UnimplementedKVServer) RangeStream(*RangeRequest, grpc.ServerStreamingServer[RangeStreamResponse]) error {
+ return status.Error(codes.Unimplemented, "method RangeStream not implemented")
+}
+func (UnimplementedKVServer) Put(context.Context, *PutRequest) (*PutResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Put not implemented")
+}
+func (UnimplementedKVServer) DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method DeleteRange not implemented")
+}
+func (UnimplementedKVServer) Txn(context.Context, *TxnRequest) (*TxnResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Txn not implemented")
+}
+func (UnimplementedKVServer) Compact(context.Context, *CompactionRequest) (*CompactionResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Compact not implemented")
+}
+func (UnimplementedKVServer) mustEmbedUnimplementedKVServer() {}
+func (UnimplementedKVServer) testEmbeddedByValue() {}
+
+// UnsafeKVServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to KVServer will
+// result in compilation errors.
+type UnsafeKVServer interface {
+ mustEmbedUnimplementedKVServer()
+}
+
+func RegisterKVServer(s grpc.ServiceRegistrar, srv KVServer) {
+ // If the following call panics, it indicates UnimplementedKVServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&KV_ServiceDesc, srv)
+}
+
+func _KV_Range_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(RangeRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KVServer).Range(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: KV_Range_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KVServer).Range(ctx, req.(*RangeRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _KV_RangeStream_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(RangeRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(KVServer).RangeStream(m, &grpc.GenericServerStream[RangeRequest, RangeStreamResponse]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type KV_RangeStreamServer = grpc.ServerStreamingServer[RangeStreamResponse]
+
+func _KV_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(PutRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KVServer).Put(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: KV_Put_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KVServer).Put(ctx, req.(*PutRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _KV_DeleteRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DeleteRangeRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KVServer).DeleteRange(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: KV_DeleteRange_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KVServer).DeleteRange(ctx, req.(*DeleteRangeRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _KV_Txn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(TxnRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KVServer).Txn(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: KV_Txn_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KVServer).Txn(ctx, req.(*TxnRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _KV_Compact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(CompactionRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(KVServer).Compact(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: KV_Compact_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(KVServer).Compact(ctx, req.(*CompactionRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// KV_ServiceDesc is the grpc.ServiceDesc for KV service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var KV_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.KV",
+ HandlerType: (*KVServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Range",
+ Handler: _KV_Range_Handler,
+ },
+ {
+ MethodName: "Put",
+ Handler: _KV_Put_Handler,
+ },
+ {
+ MethodName: "DeleteRange",
+ Handler: _KV_DeleteRange_Handler,
+ },
+ {
+ MethodName: "Txn",
+ Handler: _KV_Txn_Handler,
+ },
+ {
+ MethodName: "Compact",
+ Handler: _KV_Compact_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "RangeStream",
+ Handler: _KV_RangeStream_Handler,
+ ServerStreams: true,
+ },
+ },
+ Metadata: "rpc.proto",
+}
+
+const (
+ Watch_Watch_FullMethodName = "/etcdserverpb.Watch/Watch"
+)
+
+// WatchClient is the client API for Watch service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type WatchClient interface {
+ // Watch watches for events happening or that have happened. Both input and output
+ // are streams; the input stream is for creating and canceling watchers and the output
+ // stream sends events. One watch RPC can watch on multiple key ranges, streaming events
+ // for several watches at once. The entire event history can be watched starting from the
+ // last compaction revision.
+ Watch(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[WatchRequest, WatchResponse], error)
+}
+
+type watchClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewWatchClient(cc grpc.ClientConnInterface) WatchClient {
+ return &watchClient{cc}
+}
+
+func (c *watchClient) Watch(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[WatchRequest, WatchResponse], error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &Watch_ServiceDesc.Streams[0], Watch_Watch_FullMethodName, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &grpc.GenericClientStream[WatchRequest, WatchResponse]{ClientStream: stream}
+ return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Watch_WatchClient = grpc.BidiStreamingClient[WatchRequest, WatchResponse]
+
+// WatchServer is the server API for Watch service.
+// All implementations must embed UnimplementedWatchServer
+// for forward compatibility.
+type WatchServer interface {
+ // Watch watches for events happening or that have happened. Both input and output
+ // are streams; the input stream is for creating and canceling watchers and the output
+ // stream sends events. One watch RPC can watch on multiple key ranges, streaming events
+ // for several watches at once. The entire event history can be watched starting from the
+ // last compaction revision.
+ Watch(grpc.BidiStreamingServer[WatchRequest, WatchResponse]) error
+ mustEmbedUnimplementedWatchServer()
+}
+
+// UnimplementedWatchServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedWatchServer struct{}
+
+func (UnimplementedWatchServer) Watch(grpc.BidiStreamingServer[WatchRequest, WatchResponse]) error {
+ return status.Error(codes.Unimplemented, "method Watch not implemented")
+}
+func (UnimplementedWatchServer) mustEmbedUnimplementedWatchServer() {}
+func (UnimplementedWatchServer) testEmbeddedByValue() {}
+
+// UnsafeWatchServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to WatchServer will
+// result in compilation errors.
+type UnsafeWatchServer interface {
+ mustEmbedUnimplementedWatchServer()
+}
+
+func RegisterWatchServer(s grpc.ServiceRegistrar, srv WatchServer) {
+ // If the following call panics, it indicates UnimplementedWatchServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Watch_ServiceDesc, srv)
+}
+
+func _Watch_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(WatchServer).Watch(&grpc.GenericServerStream[WatchRequest, WatchResponse]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Watch_WatchServer = grpc.BidiStreamingServer[WatchRequest, WatchResponse]
+
+// Watch_ServiceDesc is the grpc.ServiceDesc for Watch service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Watch_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.Watch",
+ HandlerType: (*WatchServer)(nil),
+ Methods: []grpc.MethodDesc{},
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "Watch",
+ Handler: _Watch_Watch_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+ Metadata: "rpc.proto",
+}
+
+const (
+ Lease_LeaseGrant_FullMethodName = "/etcdserverpb.Lease/LeaseGrant"
+ Lease_LeaseRevoke_FullMethodName = "/etcdserverpb.Lease/LeaseRevoke"
+ Lease_LeaseKeepAlive_FullMethodName = "/etcdserverpb.Lease/LeaseKeepAlive"
+ Lease_LeaseTimeToLive_FullMethodName = "/etcdserverpb.Lease/LeaseTimeToLive"
+ Lease_LeaseLeases_FullMethodName = "/etcdserverpb.Lease/LeaseLeases"
+)
+
+// LeaseClient is the client API for Lease service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type LeaseClient interface {
+ // LeaseGrant creates a lease which expires if the server does not receive a keepAlive
+ // within a given time to live period. All keys attached to the lease will be expired and
+ // deleted if the lease expires. Each expired key generates a delete event in the event history.
+ LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error)
+ // LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
+ LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error)
+ // LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
+ // to the server and streaming keep alive responses from the server to the client.
+ LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse], error)
+ // LeaseTimeToLive retrieves lease information.
+ LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error)
+ // LeaseLeases lists all existing leases.
+ LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error)
+}
+
+type leaseClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewLeaseClient(cc grpc.ClientConnInterface) LeaseClient {
+ return &leaseClient{cc}
+}
+
+func (c *leaseClient) LeaseGrant(ctx context.Context, in *LeaseGrantRequest, opts ...grpc.CallOption) (*LeaseGrantResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LeaseGrantResponse)
+ err := c.cc.Invoke(ctx, Lease_LeaseGrant_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *leaseClient) LeaseRevoke(ctx context.Context, in *LeaseRevokeRequest, opts ...grpc.CallOption) (*LeaseRevokeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LeaseRevokeResponse)
+ err := c.cc.Invoke(ctx, Lease_LeaseRevoke_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *leaseClient) LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse], error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &Lease_ServiceDesc.Streams[0], Lease_LeaseKeepAlive_FullMethodName, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &grpc.GenericClientStream[LeaseKeepAliveRequest, LeaseKeepAliveResponse]{ClientStream: stream}
+ return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Lease_LeaseKeepAliveClient = grpc.BidiStreamingClient[LeaseKeepAliveRequest, LeaseKeepAliveResponse]
+
+func (c *leaseClient) LeaseTimeToLive(ctx context.Context, in *LeaseTimeToLiveRequest, opts ...grpc.CallOption) (*LeaseTimeToLiveResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LeaseTimeToLiveResponse)
+ err := c.cc.Invoke(ctx, Lease_LeaseTimeToLive_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *leaseClient) LeaseLeases(ctx context.Context, in *LeaseLeasesRequest, opts ...grpc.CallOption) (*LeaseLeasesResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LeaseLeasesResponse)
+ err := c.cc.Invoke(ctx, Lease_LeaseLeases_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// LeaseServer is the server API for Lease service.
+// All implementations must embed UnimplementedLeaseServer
+// for forward compatibility.
+type LeaseServer interface {
+ // LeaseGrant creates a lease which expires if the server does not receive a keepAlive
+ // within a given time to live period. All keys attached to the lease will be expired and
+ // deleted if the lease expires. Each expired key generates a delete event in the event history.
+ LeaseGrant(context.Context, *LeaseGrantRequest) (*LeaseGrantResponse, error)
+ // LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
+ LeaseRevoke(context.Context, *LeaseRevokeRequest) (*LeaseRevokeResponse, error)
+ // LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
+ // to the server and streaming keep alive responses from the server to the client.
+ LeaseKeepAlive(grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]) error
+ // LeaseTimeToLive retrieves lease information.
+ LeaseTimeToLive(context.Context, *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error)
+ // LeaseLeases lists all existing leases.
+ LeaseLeases(context.Context, *LeaseLeasesRequest) (*LeaseLeasesResponse, error)
+ mustEmbedUnimplementedLeaseServer()
+}
+
+// UnimplementedLeaseServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedLeaseServer struct{}
+
+func (UnimplementedLeaseServer) LeaseGrant(context.Context, *LeaseGrantRequest) (*LeaseGrantResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method LeaseGrant not implemented")
+}
+func (UnimplementedLeaseServer) LeaseRevoke(context.Context, *LeaseRevokeRequest) (*LeaseRevokeResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method LeaseRevoke not implemented")
+}
+func (UnimplementedLeaseServer) LeaseKeepAlive(grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]) error {
+ return status.Error(codes.Unimplemented, "method LeaseKeepAlive not implemented")
+}
+func (UnimplementedLeaseServer) LeaseTimeToLive(context.Context, *LeaseTimeToLiveRequest) (*LeaseTimeToLiveResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method LeaseTimeToLive not implemented")
+}
+func (UnimplementedLeaseServer) LeaseLeases(context.Context, *LeaseLeasesRequest) (*LeaseLeasesResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method LeaseLeases not implemented")
+}
+func (UnimplementedLeaseServer) mustEmbedUnimplementedLeaseServer() {}
+func (UnimplementedLeaseServer) testEmbeddedByValue() {}
+
+// UnsafeLeaseServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to LeaseServer will
+// result in compilation errors.
+type UnsafeLeaseServer interface {
+ mustEmbedUnimplementedLeaseServer()
+}
+
+func RegisterLeaseServer(s grpc.ServiceRegistrar, srv LeaseServer) {
+ // If the following call panics, it indicates UnimplementedLeaseServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Lease_ServiceDesc, srv)
+}
+
+func _Lease_LeaseGrant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LeaseGrantRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LeaseServer).LeaseGrant(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lease_LeaseGrant_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LeaseServer).LeaseGrant(ctx, req.(*LeaseGrantRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Lease_LeaseRevoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LeaseRevokeRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LeaseServer).LeaseRevoke(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lease_LeaseRevoke_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LeaseServer).LeaseRevoke(ctx, req.(*LeaseRevokeRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Lease_LeaseKeepAlive_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(LeaseServer).LeaseKeepAlive(&grpc.GenericServerStream[LeaseKeepAliveRequest, LeaseKeepAliveResponse]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Lease_LeaseKeepAliveServer = grpc.BidiStreamingServer[LeaseKeepAliveRequest, LeaseKeepAliveResponse]
+
+func _Lease_LeaseTimeToLive_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LeaseTimeToLiveRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LeaseServer).LeaseTimeToLive(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lease_LeaseTimeToLive_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LeaseServer).LeaseTimeToLive(ctx, req.(*LeaseTimeToLiveRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Lease_LeaseLeases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LeaseLeasesRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LeaseServer).LeaseLeases(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lease_LeaseLeases_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LeaseServer).LeaseLeases(ctx, req.(*LeaseLeasesRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Lease_ServiceDesc is the grpc.ServiceDesc for Lease service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Lease_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.Lease",
+ HandlerType: (*LeaseServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "LeaseGrant",
+ Handler: _Lease_LeaseGrant_Handler,
+ },
+ {
+ MethodName: "LeaseRevoke",
+ Handler: _Lease_LeaseRevoke_Handler,
+ },
+ {
+ MethodName: "LeaseTimeToLive",
+ Handler: _Lease_LeaseTimeToLive_Handler,
+ },
+ {
+ MethodName: "LeaseLeases",
+ Handler: _Lease_LeaseLeases_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "LeaseKeepAlive",
+ Handler: _Lease_LeaseKeepAlive_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+ Metadata: "rpc.proto",
+}
+
+const (
+ Cluster_MemberAdd_FullMethodName = "/etcdserverpb.Cluster/MemberAdd"
+ Cluster_MemberRemove_FullMethodName = "/etcdserverpb.Cluster/MemberRemove"
+ Cluster_MemberUpdate_FullMethodName = "/etcdserverpb.Cluster/MemberUpdate"
+ Cluster_MemberList_FullMethodName = "/etcdserverpb.Cluster/MemberList"
+ Cluster_MemberPromote_FullMethodName = "/etcdserverpb.Cluster/MemberPromote"
+)
+
+// ClusterClient is the client API for Cluster service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type ClusterClient interface {
+ // MemberAdd adds a member into the cluster.
+ MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error)
+ // MemberRemove removes an existing member from the cluster.
+ MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error)
+ // MemberUpdate updates the member configuration.
+ MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error)
+ // MemberList lists all the members in the cluster.
+ MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error)
+ // MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
+ MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error)
+}
+
+type clusterClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewClusterClient(cc grpc.ClientConnInterface) ClusterClient {
+ return &clusterClient{cc}
+}
+
+func (c *clusterClient) MemberAdd(ctx context.Context, in *MemberAddRequest, opts ...grpc.CallOption) (*MemberAddResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MemberAddResponse)
+ err := c.cc.Invoke(ctx, Cluster_MemberAdd_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *clusterClient) MemberRemove(ctx context.Context, in *MemberRemoveRequest, opts ...grpc.CallOption) (*MemberRemoveResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MemberRemoveResponse)
+ err := c.cc.Invoke(ctx, Cluster_MemberRemove_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *clusterClient) MemberUpdate(ctx context.Context, in *MemberUpdateRequest, opts ...grpc.CallOption) (*MemberUpdateResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MemberUpdateResponse)
+ err := c.cc.Invoke(ctx, Cluster_MemberUpdate_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *clusterClient) MemberList(ctx context.Context, in *MemberListRequest, opts ...grpc.CallOption) (*MemberListResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MemberListResponse)
+ err := c.cc.Invoke(ctx, Cluster_MemberList_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *clusterClient) MemberPromote(ctx context.Context, in *MemberPromoteRequest, opts ...grpc.CallOption) (*MemberPromoteResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MemberPromoteResponse)
+ err := c.cc.Invoke(ctx, Cluster_MemberPromote_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// ClusterServer is the server API for Cluster service.
+// All implementations must embed UnimplementedClusterServer
+// for forward compatibility.
+type ClusterServer interface {
+ // MemberAdd adds a member into the cluster.
+ MemberAdd(context.Context, *MemberAddRequest) (*MemberAddResponse, error)
+ // MemberRemove removes an existing member from the cluster.
+ MemberRemove(context.Context, *MemberRemoveRequest) (*MemberRemoveResponse, error)
+ // MemberUpdate updates the member configuration.
+ MemberUpdate(context.Context, *MemberUpdateRequest) (*MemberUpdateResponse, error)
+ // MemberList lists all the members in the cluster.
+ MemberList(context.Context, *MemberListRequest) (*MemberListResponse, error)
+ // MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
+ MemberPromote(context.Context, *MemberPromoteRequest) (*MemberPromoteResponse, error)
+ mustEmbedUnimplementedClusterServer()
+}
+
+// UnimplementedClusterServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedClusterServer struct{}
+
+func (UnimplementedClusterServer) MemberAdd(context.Context, *MemberAddRequest) (*MemberAddResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MemberAdd not implemented")
+}
+func (UnimplementedClusterServer) MemberRemove(context.Context, *MemberRemoveRequest) (*MemberRemoveResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MemberRemove not implemented")
+}
+func (UnimplementedClusterServer) MemberUpdate(context.Context, *MemberUpdateRequest) (*MemberUpdateResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MemberUpdate not implemented")
+}
+func (UnimplementedClusterServer) MemberList(context.Context, *MemberListRequest) (*MemberListResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MemberList not implemented")
+}
+func (UnimplementedClusterServer) MemberPromote(context.Context, *MemberPromoteRequest) (*MemberPromoteResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MemberPromote not implemented")
+}
+func (UnimplementedClusterServer) mustEmbedUnimplementedClusterServer() {}
+func (UnimplementedClusterServer) testEmbeddedByValue() {}
+
+// UnsafeClusterServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ClusterServer will
+// result in compilation errors.
+type UnsafeClusterServer interface {
+ mustEmbedUnimplementedClusterServer()
+}
+
+func RegisterClusterServer(s grpc.ServiceRegistrar, srv ClusterServer) {
+ // If the following call panics, it indicates UnimplementedClusterServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Cluster_ServiceDesc, srv)
+}
+
+func _Cluster_MemberAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MemberAddRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ClusterServer).MemberAdd(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Cluster_MemberAdd_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ClusterServer).MemberAdd(ctx, req.(*MemberAddRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Cluster_MemberRemove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MemberRemoveRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ClusterServer).MemberRemove(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Cluster_MemberRemove_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ClusterServer).MemberRemove(ctx, req.(*MemberRemoveRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Cluster_MemberUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MemberUpdateRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ClusterServer).MemberUpdate(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Cluster_MemberUpdate_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ClusterServer).MemberUpdate(ctx, req.(*MemberUpdateRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Cluster_MemberList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MemberListRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ClusterServer).MemberList(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Cluster_MemberList_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ClusterServer).MemberList(ctx, req.(*MemberListRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Cluster_MemberPromote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MemberPromoteRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ClusterServer).MemberPromote(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Cluster_MemberPromote_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ClusterServer).MemberPromote(ctx, req.(*MemberPromoteRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Cluster_ServiceDesc is the grpc.ServiceDesc for Cluster service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Cluster_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.Cluster",
+ HandlerType: (*ClusterServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "MemberAdd",
+ Handler: _Cluster_MemberAdd_Handler,
+ },
+ {
+ MethodName: "MemberRemove",
+ Handler: _Cluster_MemberRemove_Handler,
+ },
+ {
+ MethodName: "MemberUpdate",
+ Handler: _Cluster_MemberUpdate_Handler,
+ },
+ {
+ MethodName: "MemberList",
+ Handler: _Cluster_MemberList_Handler,
+ },
+ {
+ MethodName: "MemberPromote",
+ Handler: _Cluster_MemberPromote_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "rpc.proto",
+}
+
+const (
+ Maintenance_Alarm_FullMethodName = "/etcdserverpb.Maintenance/Alarm"
+ Maintenance_Status_FullMethodName = "/etcdserverpb.Maintenance/Status"
+ Maintenance_Defragment_FullMethodName = "/etcdserverpb.Maintenance/Defragment"
+ Maintenance_Hash_FullMethodName = "/etcdserverpb.Maintenance/Hash"
+ Maintenance_HashKV_FullMethodName = "/etcdserverpb.Maintenance/HashKV"
+ Maintenance_Snapshot_FullMethodName = "/etcdserverpb.Maintenance/Snapshot"
+ Maintenance_MoveLeader_FullMethodName = "/etcdserverpb.Maintenance/MoveLeader"
+ Maintenance_Downgrade_FullMethodName = "/etcdserverpb.Maintenance/Downgrade"
+)
+
+// MaintenanceClient is the client API for Maintenance service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type MaintenanceClient interface {
+ // Alarm activates, deactivates, and queries alarms regarding cluster health.
+ Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error)
+ // Status gets the status of the member.
+ Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
+ // Defragment defragments a member's backend database to recover storage space.
+ Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error)
+ // Hash computes the hash of whole backend keyspace,
+ // including key, lease, and other buckets in storage.
+ // This is designed for testing ONLY!
+ // Do not rely on this in production with ongoing transactions,
+ // since Hash operation does not hold MVCC locks.
+ // Use "HashKV" API instead for "key" bucket consistency checks.
+ Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error)
+ // HashKV computes the hash of all MVCC keys up to a given revision.
+ // It only iterates "key" bucket in backend storage.
+ HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error)
+ // Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
+ Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SnapshotResponse], error)
+ // MoveLeader requests current leader node to transfer its leadership to transferee.
+ MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error)
+ // Downgrade requests downgrades, verifies feasibility or cancels downgrade
+ // on the cluster version.
+ // Supported since etcd 3.5.
+ Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error)
+}
+
+type maintenanceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewMaintenanceClient(cc grpc.ClientConnInterface) MaintenanceClient {
+ return &maintenanceClient{cc}
+}
+
+func (c *maintenanceClient) Alarm(ctx context.Context, in *AlarmRequest, opts ...grpc.CallOption) (*AlarmResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AlarmResponse)
+ err := c.cc.Invoke(ctx, Maintenance_Alarm_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(StatusResponse)
+ err := c.cc.Invoke(ctx, Maintenance_Status_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) Defragment(ctx context.Context, in *DefragmentRequest, opts ...grpc.CallOption) (*DefragmentResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(DefragmentResponse)
+ err := c.cc.Invoke(ctx, Maintenance_Defragment_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) Hash(ctx context.Context, in *HashRequest, opts ...grpc.CallOption) (*HashResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(HashResponse)
+ err := c.cc.Invoke(ctx, Maintenance_Hash_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) HashKV(ctx context.Context, in *HashKVRequest, opts ...grpc.CallOption) (*HashKVResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(HashKVResponse)
+ err := c.cc.Invoke(ctx, Maintenance_HashKV_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) Snapshot(ctx context.Context, in *SnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SnapshotResponse], error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &Maintenance_ServiceDesc.Streams[0], Maintenance_Snapshot_FullMethodName, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &grpc.GenericClientStream[SnapshotRequest, SnapshotResponse]{ClientStream: stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Maintenance_SnapshotClient = grpc.ServerStreamingClient[SnapshotResponse]
+
+func (c *maintenanceClient) MoveLeader(ctx context.Context, in *MoveLeaderRequest, opts ...grpc.CallOption) (*MoveLeaderResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(MoveLeaderResponse)
+ err := c.cc.Invoke(ctx, Maintenance_MoveLeader_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *maintenanceClient) Downgrade(ctx context.Context, in *DowngradeRequest, opts ...grpc.CallOption) (*DowngradeResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(DowngradeResponse)
+ err := c.cc.Invoke(ctx, Maintenance_Downgrade_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// MaintenanceServer is the server API for Maintenance service.
+// All implementations must embed UnimplementedMaintenanceServer
+// for forward compatibility.
+type MaintenanceServer interface {
+ // Alarm activates, deactivates, and queries alarms regarding cluster health.
+ Alarm(context.Context, *AlarmRequest) (*AlarmResponse, error)
+ // Status gets the status of the member.
+ Status(context.Context, *StatusRequest) (*StatusResponse, error)
+ // Defragment defragments a member's backend database to recover storage space.
+ Defragment(context.Context, *DefragmentRequest) (*DefragmentResponse, error)
+ // Hash computes the hash of whole backend keyspace,
+ // including key, lease, and other buckets in storage.
+ // This is designed for testing ONLY!
+ // Do not rely on this in production with ongoing transactions,
+ // since Hash operation does not hold MVCC locks.
+ // Use "HashKV" API instead for "key" bucket consistency checks.
+ Hash(context.Context, *HashRequest) (*HashResponse, error)
+ // HashKV computes the hash of all MVCC keys up to a given revision.
+ // It only iterates "key" bucket in backend storage.
+ HashKV(context.Context, *HashKVRequest) (*HashKVResponse, error)
+ // Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
+ Snapshot(*SnapshotRequest, grpc.ServerStreamingServer[SnapshotResponse]) error
+ // MoveLeader requests current leader node to transfer its leadership to transferee.
+ MoveLeader(context.Context, *MoveLeaderRequest) (*MoveLeaderResponse, error)
+ // Downgrade requests downgrades, verifies feasibility or cancels downgrade
+ // on the cluster version.
+ // Supported since etcd 3.5.
+ Downgrade(context.Context, *DowngradeRequest) (*DowngradeResponse, error)
+ mustEmbedUnimplementedMaintenanceServer()
+}
+
+// UnimplementedMaintenanceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedMaintenanceServer struct{}
+
+func (UnimplementedMaintenanceServer) Alarm(context.Context, *AlarmRequest) (*AlarmResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Alarm not implemented")
+}
+func (UnimplementedMaintenanceServer) Status(context.Context, *StatusRequest) (*StatusResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Status not implemented")
+}
+func (UnimplementedMaintenanceServer) Defragment(context.Context, *DefragmentRequest) (*DefragmentResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Defragment not implemented")
+}
+func (UnimplementedMaintenanceServer) Hash(context.Context, *HashRequest) (*HashResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Hash not implemented")
+}
+func (UnimplementedMaintenanceServer) HashKV(context.Context, *HashKVRequest) (*HashKVResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method HashKV not implemented")
+}
+func (UnimplementedMaintenanceServer) Snapshot(*SnapshotRequest, grpc.ServerStreamingServer[SnapshotResponse]) error {
+ return status.Error(codes.Unimplemented, "method Snapshot not implemented")
+}
+func (UnimplementedMaintenanceServer) MoveLeader(context.Context, *MoveLeaderRequest) (*MoveLeaderResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method MoveLeader not implemented")
+}
+func (UnimplementedMaintenanceServer) Downgrade(context.Context, *DowngradeRequest) (*DowngradeResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Downgrade not implemented")
+}
+func (UnimplementedMaintenanceServer) mustEmbedUnimplementedMaintenanceServer() {}
+func (UnimplementedMaintenanceServer) testEmbeddedByValue() {}
+
+// UnsafeMaintenanceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to MaintenanceServer will
+// result in compilation errors.
+type UnsafeMaintenanceServer interface {
+ mustEmbedUnimplementedMaintenanceServer()
+}
+
+func RegisterMaintenanceServer(s grpc.ServiceRegistrar, srv MaintenanceServer) {
+ // If the following call panics, it indicates UnimplementedMaintenanceServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Maintenance_ServiceDesc, srv)
+}
+
+func _Maintenance_Alarm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AlarmRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).Alarm(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_Alarm_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).Alarm(ctx, req.(*AlarmRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(StatusRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).Status(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_Status_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).Status(ctx, req.(*StatusRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_Defragment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DefragmentRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).Defragment(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_Defragment_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).Defragment(ctx, req.(*DefragmentRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_Hash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(HashRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).Hash(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_Hash_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).Hash(ctx, req.(*HashRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_HashKV_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(HashKVRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).HashKV(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_HashKV_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).HashKV(ctx, req.(*HashKVRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_Snapshot_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(SnapshotRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(MaintenanceServer).Snapshot(m, &grpc.GenericServerStream[SnapshotRequest, SnapshotResponse]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Maintenance_SnapshotServer = grpc.ServerStreamingServer[SnapshotResponse]
+
+func _Maintenance_MoveLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MoveLeaderRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).MoveLeader(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_MoveLeader_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).MoveLeader(ctx, req.(*MoveLeaderRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Maintenance_Downgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DowngradeRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MaintenanceServer).Downgrade(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Maintenance_Downgrade_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MaintenanceServer).Downgrade(ctx, req.(*DowngradeRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Maintenance_ServiceDesc is the grpc.ServiceDesc for Maintenance service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Maintenance_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.Maintenance",
+ HandlerType: (*MaintenanceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Alarm",
+ Handler: _Maintenance_Alarm_Handler,
+ },
+ {
+ MethodName: "Status",
+ Handler: _Maintenance_Status_Handler,
+ },
+ {
+ MethodName: "Defragment",
+ Handler: _Maintenance_Defragment_Handler,
+ },
+ {
+ MethodName: "Hash",
+ Handler: _Maintenance_Hash_Handler,
+ },
+ {
+ MethodName: "HashKV",
+ Handler: _Maintenance_HashKV_Handler,
+ },
+ {
+ MethodName: "MoveLeader",
+ Handler: _Maintenance_MoveLeader_Handler,
+ },
+ {
+ MethodName: "Downgrade",
+ Handler: _Maintenance_Downgrade_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "Snapshot",
+ Handler: _Maintenance_Snapshot_Handler,
+ ServerStreams: true,
+ },
+ },
+ Metadata: "rpc.proto",
+}
+
+const (
+ Auth_AuthEnable_FullMethodName = "/etcdserverpb.Auth/AuthEnable"
+ Auth_AuthDisable_FullMethodName = "/etcdserverpb.Auth/AuthDisable"
+ Auth_AuthStatus_FullMethodName = "/etcdserverpb.Auth/AuthStatus"
+ Auth_Authenticate_FullMethodName = "/etcdserverpb.Auth/Authenticate"
+ Auth_UserAdd_FullMethodName = "/etcdserverpb.Auth/UserAdd"
+ Auth_UserGet_FullMethodName = "/etcdserverpb.Auth/UserGet"
+ Auth_UserList_FullMethodName = "/etcdserverpb.Auth/UserList"
+ Auth_UserDelete_FullMethodName = "/etcdserverpb.Auth/UserDelete"
+ Auth_UserChangePassword_FullMethodName = "/etcdserverpb.Auth/UserChangePassword"
+ Auth_UserGrantRole_FullMethodName = "/etcdserverpb.Auth/UserGrantRole"
+ Auth_UserRevokeRole_FullMethodName = "/etcdserverpb.Auth/UserRevokeRole"
+ Auth_RoleAdd_FullMethodName = "/etcdserverpb.Auth/RoleAdd"
+ Auth_RoleGet_FullMethodName = "/etcdserverpb.Auth/RoleGet"
+ Auth_RoleList_FullMethodName = "/etcdserverpb.Auth/RoleList"
+ Auth_RoleDelete_FullMethodName = "/etcdserverpb.Auth/RoleDelete"
+ Auth_RoleGrantPermission_FullMethodName = "/etcdserverpb.Auth/RoleGrantPermission"
+ Auth_RoleRevokePermission_FullMethodName = "/etcdserverpb.Auth/RoleRevokePermission"
+)
+
+// AuthClient is the client API for Auth service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type AuthClient interface {
+ // AuthEnable enables authentication.
+ AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error)
+ // AuthDisable disables authentication.
+ AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error)
+ // AuthStatus displays authentication status.
+ AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error)
+ // Authenticate processes an authenticate request.
+ Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error)
+ // UserAdd adds a new user. User name cannot be empty.
+ UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error)
+ // UserGet gets detailed user information.
+ UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error)
+ // UserList gets a list of all users.
+ UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error)
+ // UserDelete deletes a specified user.
+ UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error)
+ // UserChangePassword changes the password of a specified user.
+ UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error)
+ // UserGrantRole grants a role to a specified user.
+ UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error)
+ // UserRevokeRole revokes a role of specified user.
+ UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error)
+ // RoleAdd adds a new role. Role name cannot be empty.
+ RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error)
+ // RoleGet gets detailed role information.
+ RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error)
+ // RoleList gets lists of all roles.
+ RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error)
+ // RoleDelete deletes a specified role.
+ RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error)
+ // RoleGrantPermission grants a permission of a specified key or range to a specified role.
+ RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error)
+ // RoleRevokePermission revokes a key or range permission of a specified role.
+ RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error)
+}
+
+type authClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewAuthClient(cc grpc.ClientConnInterface) AuthClient {
+ return &authClient{cc}
+}
+
+func (c *authClient) AuthEnable(ctx context.Context, in *AuthEnableRequest, opts ...grpc.CallOption) (*AuthEnableResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthEnableResponse)
+ err := c.cc.Invoke(ctx, Auth_AuthEnable_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) AuthDisable(ctx context.Context, in *AuthDisableRequest, opts ...grpc.CallOption) (*AuthDisableResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthDisableResponse)
+ err := c.cc.Invoke(ctx, Auth_AuthDisable_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) AuthStatus(ctx context.Context, in *AuthStatusRequest, opts ...grpc.CallOption) (*AuthStatusResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthStatusResponse)
+ err := c.cc.Invoke(ctx, Auth_AuthStatus_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) Authenticate(ctx context.Context, in *AuthenticateRequest, opts ...grpc.CallOption) (*AuthenticateResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthenticateResponse)
+ err := c.cc.Invoke(ctx, Auth_Authenticate_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserAdd(ctx context.Context, in *AuthUserAddRequest, opts ...grpc.CallOption) (*AuthUserAddResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserAddResponse)
+ err := c.cc.Invoke(ctx, Auth_UserAdd_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserGet(ctx context.Context, in *AuthUserGetRequest, opts ...grpc.CallOption) (*AuthUserGetResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserGetResponse)
+ err := c.cc.Invoke(ctx, Auth_UserGet_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserList(ctx context.Context, in *AuthUserListRequest, opts ...grpc.CallOption) (*AuthUserListResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserListResponse)
+ err := c.cc.Invoke(ctx, Auth_UserList_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserDelete(ctx context.Context, in *AuthUserDeleteRequest, opts ...grpc.CallOption) (*AuthUserDeleteResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserDeleteResponse)
+ err := c.cc.Invoke(ctx, Auth_UserDelete_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserChangePassword(ctx context.Context, in *AuthUserChangePasswordRequest, opts ...grpc.CallOption) (*AuthUserChangePasswordResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserChangePasswordResponse)
+ err := c.cc.Invoke(ctx, Auth_UserChangePassword_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserGrantRole(ctx context.Context, in *AuthUserGrantRoleRequest, opts ...grpc.CallOption) (*AuthUserGrantRoleResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserGrantRoleResponse)
+ err := c.cc.Invoke(ctx, Auth_UserGrantRole_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) UserRevokeRole(ctx context.Context, in *AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (*AuthUserRevokeRoleResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthUserRevokeRoleResponse)
+ err := c.cc.Invoke(ctx, Auth_UserRevokeRole_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleAdd(ctx context.Context, in *AuthRoleAddRequest, opts ...grpc.CallOption) (*AuthRoleAddResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleAddResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleAdd_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleGet(ctx context.Context, in *AuthRoleGetRequest, opts ...grpc.CallOption) (*AuthRoleGetResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleGetResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleGet_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleList(ctx context.Context, in *AuthRoleListRequest, opts ...grpc.CallOption) (*AuthRoleListResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleListResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleList_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleDelete(ctx context.Context, in *AuthRoleDeleteRequest, opts ...grpc.CallOption) (*AuthRoleDeleteResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleDeleteResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleDelete_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleGrantPermission(ctx context.Context, in *AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (*AuthRoleGrantPermissionResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleGrantPermissionResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleGrantPermission_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *authClient) RoleRevokePermission(ctx context.Context, in *AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (*AuthRoleRevokePermissionResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(AuthRoleRevokePermissionResponse)
+ err := c.cc.Invoke(ctx, Auth_RoleRevokePermission_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// AuthServer is the server API for Auth service.
+// All implementations must embed UnimplementedAuthServer
+// for forward compatibility.
+type AuthServer interface {
+ // AuthEnable enables authentication.
+ AuthEnable(context.Context, *AuthEnableRequest) (*AuthEnableResponse, error)
+ // AuthDisable disables authentication.
+ AuthDisable(context.Context, *AuthDisableRequest) (*AuthDisableResponse, error)
+ // AuthStatus displays authentication status.
+ AuthStatus(context.Context, *AuthStatusRequest) (*AuthStatusResponse, error)
+ // Authenticate processes an authenticate request.
+ Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error)
+ // UserAdd adds a new user. User name cannot be empty.
+ UserAdd(context.Context, *AuthUserAddRequest) (*AuthUserAddResponse, error)
+ // UserGet gets detailed user information.
+ UserGet(context.Context, *AuthUserGetRequest) (*AuthUserGetResponse, error)
+ // UserList gets a list of all users.
+ UserList(context.Context, *AuthUserListRequest) (*AuthUserListResponse, error)
+ // UserDelete deletes a specified user.
+ UserDelete(context.Context, *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error)
+ // UserChangePassword changes the password of a specified user.
+ UserChangePassword(context.Context, *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error)
+ // UserGrantRole grants a role to a specified user.
+ UserGrantRole(context.Context, *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error)
+ // UserRevokeRole revokes a role of specified user.
+ UserRevokeRole(context.Context, *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error)
+ // RoleAdd adds a new role. Role name cannot be empty.
+ RoleAdd(context.Context, *AuthRoleAddRequest) (*AuthRoleAddResponse, error)
+ // RoleGet gets detailed role information.
+ RoleGet(context.Context, *AuthRoleGetRequest) (*AuthRoleGetResponse, error)
+ // RoleList gets lists of all roles.
+ RoleList(context.Context, *AuthRoleListRequest) (*AuthRoleListResponse, error)
+ // RoleDelete deletes a specified role.
+ RoleDelete(context.Context, *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error)
+ // RoleGrantPermission grants a permission of a specified key or range to a specified role.
+ RoleGrantPermission(context.Context, *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error)
+ // RoleRevokePermission revokes a key or range permission of a specified role.
+ RoleRevokePermission(context.Context, *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error)
+ mustEmbedUnimplementedAuthServer()
+}
+
+// UnimplementedAuthServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedAuthServer struct{}
+
+func (UnimplementedAuthServer) AuthEnable(context.Context, *AuthEnableRequest) (*AuthEnableResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method AuthEnable not implemented")
+}
+func (UnimplementedAuthServer) AuthDisable(context.Context, *AuthDisableRequest) (*AuthDisableResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method AuthDisable not implemented")
+}
+func (UnimplementedAuthServer) AuthStatus(context.Context, *AuthStatusRequest) (*AuthStatusResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method AuthStatus not implemented")
+}
+func (UnimplementedAuthServer) Authenticate(context.Context, *AuthenticateRequest) (*AuthenticateResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Authenticate not implemented")
+}
+func (UnimplementedAuthServer) UserAdd(context.Context, *AuthUserAddRequest) (*AuthUserAddResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserAdd not implemented")
+}
+func (UnimplementedAuthServer) UserGet(context.Context, *AuthUserGetRequest) (*AuthUserGetResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserGet not implemented")
+}
+func (UnimplementedAuthServer) UserList(context.Context, *AuthUserListRequest) (*AuthUserListResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserList not implemented")
+}
+func (UnimplementedAuthServer) UserDelete(context.Context, *AuthUserDeleteRequest) (*AuthUserDeleteResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserDelete not implemented")
+}
+func (UnimplementedAuthServer) UserChangePassword(context.Context, *AuthUserChangePasswordRequest) (*AuthUserChangePasswordResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserChangePassword not implemented")
+}
+func (UnimplementedAuthServer) UserGrantRole(context.Context, *AuthUserGrantRoleRequest) (*AuthUserGrantRoleResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserGrantRole not implemented")
+}
+func (UnimplementedAuthServer) UserRevokeRole(context.Context, *AuthUserRevokeRoleRequest) (*AuthUserRevokeRoleResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method UserRevokeRole not implemented")
+}
+func (UnimplementedAuthServer) RoleAdd(context.Context, *AuthRoleAddRequest) (*AuthRoleAddResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleAdd not implemented")
+}
+func (UnimplementedAuthServer) RoleGet(context.Context, *AuthRoleGetRequest) (*AuthRoleGetResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleGet not implemented")
+}
+func (UnimplementedAuthServer) RoleList(context.Context, *AuthRoleListRequest) (*AuthRoleListResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleList not implemented")
+}
+func (UnimplementedAuthServer) RoleDelete(context.Context, *AuthRoleDeleteRequest) (*AuthRoleDeleteResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleDelete not implemented")
+}
+func (UnimplementedAuthServer) RoleGrantPermission(context.Context, *AuthRoleGrantPermissionRequest) (*AuthRoleGrantPermissionResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleGrantPermission not implemented")
+}
+func (UnimplementedAuthServer) RoleRevokePermission(context.Context, *AuthRoleRevokePermissionRequest) (*AuthRoleRevokePermissionResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method RoleRevokePermission not implemented")
+}
+func (UnimplementedAuthServer) mustEmbedUnimplementedAuthServer() {}
+func (UnimplementedAuthServer) testEmbeddedByValue() {}
+
+// UnsafeAuthServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to AuthServer will
+// result in compilation errors.
+type UnsafeAuthServer interface {
+ mustEmbedUnimplementedAuthServer()
+}
+
+func RegisterAuthServer(s grpc.ServiceRegistrar, srv AuthServer) {
+ // If the following call panics, it indicates UnimplementedAuthServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Auth_ServiceDesc, srv)
+}
+
+func _Auth_AuthEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthEnableRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).AuthEnable(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_AuthEnable_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).AuthEnable(ctx, req.(*AuthEnableRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_AuthDisable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthDisableRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).AuthDisable(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_AuthDisable_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).AuthDisable(ctx, req.(*AuthDisableRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_AuthStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthStatusRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).AuthStatus(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_AuthStatus_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).AuthStatus(ctx, req.(*AuthStatusRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthenticateRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).Authenticate(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_Authenticate_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).Authenticate(ctx, req.(*AuthenticateRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserAddRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserAdd(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserAdd_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserAdd(ctx, req.(*AuthUserAddRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserGetRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserGet(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserGet_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserGet(ctx, req.(*AuthUserGetRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserListRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserList(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserList_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserList(ctx, req.(*AuthUserListRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserDeleteRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserDelete(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserDelete_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserDelete(ctx, req.(*AuthUserDeleteRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserChangePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserChangePasswordRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserChangePassword(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserChangePassword_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserChangePassword(ctx, req.(*AuthUserChangePasswordRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserGrantRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserGrantRoleRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserGrantRole(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserGrantRole_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserGrantRole(ctx, req.(*AuthUserGrantRoleRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_UserRevokeRole_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthUserRevokeRoleRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).UserRevokeRole(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_UserRevokeRole_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).UserRevokeRole(ctx, req.(*AuthUserRevokeRoleRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleAdd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleAddRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleAdd(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleAdd_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleAdd(ctx, req.(*AuthRoleAddRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleGetRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleGet(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleGet_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleGet(ctx, req.(*AuthRoleGetRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleListRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleList(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleList_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleList(ctx, req.(*AuthRoleListRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleDeleteRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleDelete(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleDelete_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleDelete(ctx, req.(*AuthRoleDeleteRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleGrantPermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleGrantPermissionRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleGrantPermission(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleGrantPermission_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleGrantPermission(ctx, req.(*AuthRoleGrantPermissionRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Auth_RoleRevokePermission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthRoleRevokePermissionRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(AuthServer).RoleRevokePermission(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Auth_RoleRevokePermission_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(AuthServer).RoleRevokePermission(ctx, req.(*AuthRoleRevokePermissionRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Auth_ServiceDesc is the grpc.ServiceDesc for Auth service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Auth_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.Auth",
+ HandlerType: (*AuthServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "AuthEnable",
+ Handler: _Auth_AuthEnable_Handler,
+ },
+ {
+ MethodName: "AuthDisable",
+ Handler: _Auth_AuthDisable_Handler,
+ },
+ {
+ MethodName: "AuthStatus",
+ Handler: _Auth_AuthStatus_Handler,
+ },
+ {
+ MethodName: "Authenticate",
+ Handler: _Auth_Authenticate_Handler,
+ },
+ {
+ MethodName: "UserAdd",
+ Handler: _Auth_UserAdd_Handler,
+ },
+ {
+ MethodName: "UserGet",
+ Handler: _Auth_UserGet_Handler,
+ },
+ {
+ MethodName: "UserList",
+ Handler: _Auth_UserList_Handler,
+ },
+ {
+ MethodName: "UserDelete",
+ Handler: _Auth_UserDelete_Handler,
+ },
+ {
+ MethodName: "UserChangePassword",
+ Handler: _Auth_UserChangePassword_Handler,
+ },
+ {
+ MethodName: "UserGrantRole",
+ Handler: _Auth_UserGrantRole_Handler,
+ },
+ {
+ MethodName: "UserRevokeRole",
+ Handler: _Auth_UserRevokeRole_Handler,
+ },
+ {
+ MethodName: "RoleAdd",
+ Handler: _Auth_RoleAdd_Handler,
+ },
+ {
+ MethodName: "RoleGet",
+ Handler: _Auth_RoleGet_Handler,
+ },
+ {
+ MethodName: "RoleList",
+ Handler: _Auth_RoleList_Handler,
+ },
+ {
+ MethodName: "RoleDelete",
+ Handler: _Auth_RoleDelete_Handler,
+ },
+ {
+ MethodName: "RoleGrantPermission",
+ Handler: _Auth_RoleGrantPermission_Handler,
+ },
+ {
+ MethodName: "RoleRevokePermission",
+ Handler: _Auth_RoleRevokePermission_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "rpc.proto",
+}
diff --git a/api/etcdserverpb/util.go b/api/etcdserverpb/util.go
new file mode 100644
index 000000000000..413f0826b3c6
--- /dev/null
+++ b/api/etcdserverpb/util.go
@@ -0,0 +1,25 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 etcdserverpb
+
+import "google.golang.org/protobuf/proto"
+
+// Clone returns a deep copy of h, or an empty ResponseHeader if h is nil.
+func (h *ResponseHeader) Clone() *ResponseHeader {
+ if h == nil {
+ return &ResponseHeader{}
+ }
+ return proto.Clone(h).(*ResponseHeader)
+}
diff --git a/api/go.mod b/api/go.mod
index c6d6d114cc8a..aca5a9d17d5c 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -1,38 +1,27 @@
module go.etcd.io/etcd/api/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/coreos/go-semver v0.3.1
- github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.5.4
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
github.com/stretchr/testify v1.11.1
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
- google.golang.org/grpc v1.79.3
- google.golang.org/protobuf v1.36.10
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
)
require (
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/text v0.2.0 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
-
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-replace (
- go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/api/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/pkg/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/tests/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/v3 => ./FORBIDDEN_DEPENDENCY
-)
diff --git a/api/go.sum b/api/go.sum
index bc2f296c6ef9..c150fb4fdd1a 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -3,89 +3,58 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/api/membershippb/membership.pb.go b/api/membershippb/membership.pb.go
index 85a2a741f31e..1882611f0217 100644
--- a/api/membershippb/membership.pb.go
+++ b/api/membershippb/membership.pb.go
@@ -1,1459 +1,423 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: membership.proto
package membershippb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
_ "go.etcd.io/etcd/api/v3/versionpb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
// RaftAttributes represents the raft related attributes of an etcd member.
type RaftAttributes struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// peerURLs is the list of peers in the raft cluster.
PeerUrls []string `protobuf:"bytes,1,rep,name=peer_urls,json=peerUrls,proto3" json:"peer_urls,omitempty"`
// isLearner indicates if the member is raft learner.
- IsLearner bool `protobuf:"varint,2,opt,name=is_learner,json=isLearner,proto3" json:"is_learner,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ IsLearner bool `protobuf:"varint,2,opt,name=is_learner,json=isLearner,proto3" json:"is_learner,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *RaftAttributes) Reset() { *m = RaftAttributes{} }
-func (m *RaftAttributes) String() string { return proto.CompactTextString(m) }
-func (*RaftAttributes) ProtoMessage() {}
-func (*RaftAttributes) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{0}
-}
-func (m *RaftAttributes) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *RaftAttributes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_RaftAttributes.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *RaftAttributes) XXX_Merge(src proto.Message) {
- xxx_messageInfo_RaftAttributes.Merge(m, src)
-}
-func (m *RaftAttributes) XXX_Size() int {
- return m.Size()
+func (x *RaftAttributes) Reset() {
+ *x = RaftAttributes{}
+ mi := &file_membership_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *RaftAttributes) XXX_DiscardUnknown() {
- xxx_messageInfo_RaftAttributes.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_RaftAttributes proto.InternalMessageInfo
-// Attributes represents all the non-raft related attributes of an etcd member.
-type Attributes struct {
- Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
- ClientUrls []string `protobuf:"bytes,2,rep,name=client_urls,json=clientUrls,proto3" json:"client_urls,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *Attributes) Reset() { *m = Attributes{} }
-func (m *Attributes) String() string { return proto.CompactTextString(m) }
-func (*Attributes) ProtoMessage() {}
-func (*Attributes) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{1}
-}
-func (m *Attributes) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Attributes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Attributes.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Attributes) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Attributes.Merge(m, src)
-}
-func (m *Attributes) XXX_Size() int {
- return m.Size()
-}
-func (m *Attributes) XXX_DiscardUnknown() {
- xxx_messageInfo_Attributes.DiscardUnknown(m)
+func (x *RaftAttributes) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-var xxx_messageInfo_Attributes proto.InternalMessageInfo
+func (*RaftAttributes) ProtoMessage() {}
-type Member struct {
- ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- RaftAttributes *RaftAttributes `protobuf:"bytes,2,opt,name=raft_attributes,json=raftAttributes,proto3" json:"raft_attributes,omitempty"`
- MemberAttributes *Attributes `protobuf:"bytes,3,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *Member) Reset() { *m = Member{} }
-func (m *Member) String() string { return proto.CompactTextString(m) }
-func (*Member) ProtoMessage() {}
-func (*Member) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{2}
-}
-func (m *Member) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Member) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Member.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *RaftAttributes) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *Member) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Member.Merge(m, src)
-}
-func (m *Member) XXX_Size() int {
- return m.Size()
-}
-func (m *Member) XXX_DiscardUnknown() {
- xxx_messageInfo_Member.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_Member proto.InternalMessageInfo
-
-type ClusterVersionSetRequest struct {
- Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Deprecated: Use RaftAttributes.ProtoReflect.Descriptor instead.
+func (*RaftAttributes) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{0}
}
-func (m *ClusterVersionSetRequest) Reset() { *m = ClusterVersionSetRequest{} }
-func (m *ClusterVersionSetRequest) String() string { return proto.CompactTextString(m) }
-func (*ClusterVersionSetRequest) ProtoMessage() {}
-func (*ClusterVersionSetRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{3}
-}
-func (m *ClusterVersionSetRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *ClusterVersionSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_ClusterVersionSetRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *RaftAttributes) GetPeerUrls() []string {
+ if x != nil {
+ return x.PeerUrls
}
-}
-func (m *ClusterVersionSetRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_ClusterVersionSetRequest.Merge(m, src)
-}
-func (m *ClusterVersionSetRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *ClusterVersionSetRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_ClusterVersionSetRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ClusterVersionSetRequest proto.InternalMessageInfo
-
-type ClusterMemberAttrSetRequest struct {
- Member_ID uint64 `protobuf:"varint,1,opt,name=member_ID,json=memberID,proto3" json:"member_ID,omitempty"`
- MemberAttributes *Attributes `protobuf:"bytes,2,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ return nil
}
-func (m *ClusterMemberAttrSetRequest) Reset() { *m = ClusterMemberAttrSetRequest{} }
-func (m *ClusterMemberAttrSetRequest) String() string { return proto.CompactTextString(m) }
-func (*ClusterMemberAttrSetRequest) ProtoMessage() {}
-func (*ClusterMemberAttrSetRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{4}
-}
-func (m *ClusterMemberAttrSetRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *ClusterMemberAttrSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_ClusterMemberAttrSetRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
+func (x *RaftAttributes) GetIsLearner() bool {
+ if x != nil {
+ return x.IsLearner
}
+ return false
}
-func (m *ClusterMemberAttrSetRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_ClusterMemberAttrSetRequest.Merge(m, src)
-}
-func (m *ClusterMemberAttrSetRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *ClusterMemberAttrSetRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_ClusterMemberAttrSetRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ClusterMemberAttrSetRequest proto.InternalMessageInfo
-type DowngradeInfoSetRequest struct {
- Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
- Ver string `protobuf:"bytes,2,opt,name=ver,proto3" json:"ver,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+// Attributes represents all the non-raft related attributes of an etcd member.
+type Attributes struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ ClientUrls []string `protobuf:"bytes,2,rep,name=client_urls,json=clientUrls,proto3" json:"client_urls,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *DowngradeInfoSetRequest) Reset() { *m = DowngradeInfoSetRequest{} }
-func (m *DowngradeInfoSetRequest) String() string { return proto.CompactTextString(m) }
-func (*DowngradeInfoSetRequest) ProtoMessage() {}
-func (*DowngradeInfoSetRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_949fe0d019050ef5, []int{5}
-}
-func (m *DowngradeInfoSetRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *DowngradeInfoSetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_DowngradeInfoSetRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *DowngradeInfoSetRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_DowngradeInfoSetRequest.Merge(m, src)
+func (x *Attributes) Reset() {
+ *x = Attributes{}
+ mi := &file_membership_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeInfoSetRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *DowngradeInfoSetRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_DowngradeInfoSetRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_DowngradeInfoSetRequest proto.InternalMessageInfo
-func init() {
- proto.RegisterType((*RaftAttributes)(nil), "membershippb.RaftAttributes")
- proto.RegisterType((*Attributes)(nil), "membershippb.Attributes")
- proto.RegisterType((*Member)(nil), "membershippb.Member")
- proto.RegisterType((*ClusterVersionSetRequest)(nil), "membershippb.ClusterVersionSetRequest")
- proto.RegisterType((*ClusterMemberAttrSetRequest)(nil), "membershippb.ClusterMemberAttrSetRequest")
- proto.RegisterType((*DowngradeInfoSetRequest)(nil), "membershippb.DowngradeInfoSetRequest")
+func (x *Attributes) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func init() { proto.RegisterFile("membership.proto", fileDescriptor_949fe0d019050ef5) }
-
-var fileDescriptor_949fe0d019050ef5 = []byte{
- // 422 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0xc1, 0x6e, 0xd3, 0x40,
- 0x10, 0xed, 0x3a, 0x55, 0x6b, 0x4f, 0x51, 0x28, 0x2b, 0x24, 0xac, 0x06, 0x8c, 0x55, 0x2e, 0x39,
- 0xd9, 0x12, 0x51, 0x0f, 0x70, 0x03, 0xd2, 0x43, 0x10, 0xe5, 0xb0, 0xa8, 0x1c, 0xb8, 0x44, 0xeb,
- 0x66, 0x62, 0x56, 0x72, 0xbc, 0x66, 0x77, 0x5d, 0xee, 0x1c, 0xf9, 0x02, 0xfe, 0x82, 0x13, 0xff,
- 0xd0, 0x23, 0x9f, 0x00, 0xe1, 0x47, 0x90, 0x77, 0x9d, 0xd8, 0x11, 0x9c, 0x7a, 0x1b, 0x3f, 0xcf,
- 0xbc, 0x79, 0xef, 0xed, 0xc0, 0xf1, 0x0a, 0x57, 0x19, 0x2a, 0xfd, 0x51, 0x54, 0x49, 0xa5, 0xa4,
- 0x91, 0xf4, 0x4e, 0x87, 0x54, 0xd9, 0xc9, 0xfd, 0x5c, 0xe6, 0xd2, 0xfe, 0x48, 0x9b, 0xca, 0xf5,
- 0x9c, 0xc4, 0x68, 0xae, 0x16, 0x29, 0xaf, 0x44, 0x7a, 0x8d, 0x4a, 0x0b, 0x59, 0x56, 0xd9, 0xa6,
- 0x72, 0x1d, 0xa7, 0x97, 0x30, 0x64, 0x7c, 0x69, 0x5e, 0x18, 0xa3, 0x44, 0x56, 0x1b, 0xd4, 0x74,
- 0x04, 0x41, 0x85, 0xa8, 0xe6, 0xb5, 0x2a, 0x74, 0x48, 0xe2, 0xc1, 0x38, 0x60, 0x7e, 0x03, 0x5c,
- 0xaa, 0x42, 0xd3, 0x47, 0x00, 0x42, 0xcf, 0x0b, 0xe4, 0xaa, 0x44, 0x15, 0x7a, 0x31, 0x19, 0xfb,
- 0x2c, 0x10, 0xfa, 0x8d, 0x03, 0x9e, 0x1f, 0x7e, 0xf9, 0x11, 0x0e, 0x26, 0xc9, 0xd9, 0xe9, 0x6b,
- 0x80, 0x1e, 0x25, 0x85, 0xfd, 0x92, 0xaf, 0x30, 0x24, 0x31, 0x19, 0x07, 0xcc, 0xd6, 0xf4, 0x31,
- 0x1c, 0x5d, 0x15, 0x02, 0x4b, 0xe3, 0x16, 0x79, 0x76, 0x11, 0x38, 0xa8, 0x59, 0xd5, 0x71, 0x7d,
- 0x27, 0x70, 0x70, 0x61, 0xbd, 0xd2, 0x21, 0x78, 0xb3, 0xa9, 0xa5, 0xd9, 0x67, 0xde, 0x6c, 0x4a,
- 0xcf, 0xe1, 0xae, 0xe2, 0x4b, 0x33, 0xe7, 0xdb, 0x5d, 0x56, 0xd3, 0xd1, 0xd3, 0x87, 0x49, 0x3f,
- 0x9d, 0x64, 0xd7, 0x22, 0x1b, 0xaa, 0x5d, 0xcb, 0xe7, 0x70, 0xcf, 0xb5, 0xf7, 0x89, 0x06, 0x96,
- 0x28, 0xdc, 0x25, 0xea, 0x91, 0xb4, 0x2f, 0xd2, 0x21, 0x9d, 0xe2, 0x33, 0x08, 0x5f, 0x15, 0xb5,
- 0x36, 0xa8, 0xde, 0xbb, 0xb0, 0xdf, 0xa1, 0x61, 0xf8, 0xa9, 0x46, 0x6d, 0xe8, 0x31, 0x0c, 0xae,
- 0x51, 0xb5, 0x51, 0x34, 0x65, 0x37, 0xf6, 0x95, 0xc0, 0xa8, 0x9d, 0xbb, 0xd8, 0x72, 0xf7, 0x46,
- 0x47, 0x10, 0xb4, 0x32, 0xb7, 0x21, 0xf8, 0x0e, 0xb0, 0x51, 0xfc, 0xc7, 0x83, 0x77, 0x7b, 0x0f,
- 0x6f, 0xe1, 0xc1, 0x54, 0x7e, 0x2e, 0x73, 0xc5, 0x17, 0x38, 0x2b, 0x97, 0xb2, 0xa7, 0x23, 0x84,
- 0x43, 0x2c, 0x79, 0x56, 0xe0, 0xc2, 0xaa, 0xf0, 0xd9, 0xe6, 0x73, 0x63, 0xce, 0xfb, 0xd7, 0xdc,
- 0xcb, 0x67, 0x37, 0xbf, 0xa3, 0xbd, 0x9b, 0x75, 0x44, 0x7e, 0xae, 0x23, 0xf2, 0x6b, 0x1d, 0x91,
- 0x6f, 0x7f, 0xa2, 0xbd, 0x0f, 0x4f, 0x72, 0x99, 0x34, 0x37, 0x9a, 0x08, 0x99, 0x76, 0xb7, 0x3a,
- 0x49, 0xfb, 0x82, 0xb3, 0x03, 0x7b, 0xaa, 0x93, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x56,
- 0x21, 0x97, 0x04, 0x03, 0x00, 0x00,
-}
+func (*Attributes) ProtoMessage() {}
-func (m *RaftAttributes) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Attributes) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return dAtA[:n], nil
+ return mi.MessageOf(x)
}
-func (m *RaftAttributes) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+// Deprecated: Use Attributes.ProtoReflect.Descriptor instead.
+func (*Attributes) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{1}
}
-func (m *RaftAttributes) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.IsLearner {
- i--
- if m.IsLearner {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
- }
- i--
- dAtA[i] = 0x10
- }
- if len(m.PeerUrls) > 0 {
- for iNdEx := len(m.PeerUrls) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.PeerUrls[iNdEx])
- copy(dAtA[i:], m.PeerUrls[iNdEx])
- i = encodeVarintMembership(dAtA, i, uint64(len(m.PeerUrls[iNdEx])))
- i--
- dAtA[i] = 0xa
- }
+func (x *Attributes) GetName() string {
+ if x != nil {
+ return x.Name
}
- return len(dAtA) - i, nil
+ return ""
}
-func (m *Attributes) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Attributes) GetClientUrls() []string {
+ if x != nil {
+ return x.ClientUrls
}
- return dAtA[:n], nil
+ return nil
}
-func (m *Attributes) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type Member struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ RaftAttributes *RaftAttributes `protobuf:"bytes,2,opt,name=raft_attributes,json=raftAttributes,proto3" json:"raft_attributes,omitempty"`
+ MemberAttributes *Attributes `protobuf:"bytes,3,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Attributes) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.ClientUrls) > 0 {
- for iNdEx := len(m.ClientUrls) - 1; iNdEx >= 0; iNdEx-- {
- i -= len(m.ClientUrls[iNdEx])
- copy(dAtA[i:], m.ClientUrls[iNdEx])
- i = encodeVarintMembership(dAtA, i, uint64(len(m.ClientUrls[iNdEx])))
- i--
- dAtA[i] = 0x12
- }
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintMembership(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *Member) Reset() {
+ *x = Member{}
+ mi := &file_membership_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Member) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *Member) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Member) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*Member) ProtoMessage() {}
-func (m *Member) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.MemberAttributes != nil {
- {
- size, err := m.MemberAttributes.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintMembership(dAtA, i, uint64(size))
+func (x *Member) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x1a
- }
- if m.RaftAttributes != nil {
- {
- size, err := m.RaftAttributes.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintMembership(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.ID != 0 {
- i = encodeVarintMembership(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
+ return ms
}
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func (m *ClusterVersionSetRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+// Deprecated: Use Member.ProtoReflect.Descriptor instead.
+func (*Member) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{2}
}
-func (m *ClusterVersionSetRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *Member) GetID() uint64 {
+ if x != nil {
+ return x.ID
+ }
+ return 0
}
-func (m *ClusterVersionSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
+func (x *Member) GetRaftAttributes() *RaftAttributes {
+ if x != nil {
+ return x.RaftAttributes
}
- if len(m.Ver) > 0 {
- i -= len(m.Ver)
- copy(dAtA[i:], m.Ver)
- i = encodeVarintMembership(dAtA, i, uint64(len(m.Ver)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+ return nil
}
-func (m *ClusterMemberAttrSetRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Member) GetMemberAttributes() *Attributes {
+ if x != nil {
+ return x.MemberAttributes
}
- return dAtA[:n], nil
+ return nil
}
-func (m *ClusterMemberAttrSetRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type ClusterVersionSetRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Ver string `protobuf:"bytes,1,opt,name=ver,proto3" json:"ver,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ClusterMemberAttrSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.MemberAttributes != nil {
- {
- size, err := m.MemberAttributes.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintMembership(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.Member_ID != 0 {
- i = encodeVarintMembership(dAtA, i, uint64(m.Member_ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
+func (x *ClusterVersionSetRequest) Reset() {
+ *x = ClusterVersionSetRequest{}
+ mi := &file_membership_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeInfoSetRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *ClusterVersionSetRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeInfoSetRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*ClusterVersionSetRequest) ProtoMessage() {}
-func (m *DowngradeInfoSetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Ver) > 0 {
- i -= len(m.Ver)
- copy(dAtA[i:], m.Ver)
- i = encodeVarintMembership(dAtA, i, uint64(len(m.Ver)))
- i--
- dAtA[i] = 0x12
- }
- if m.Enabled {
- i--
- if m.Enabled {
- dAtA[i] = 1
- } else {
- dAtA[i] = 0
+func (x *ClusterVersionSetRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x8
+ return ms
}
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func encodeVarintMembership(dAtA []byte, offset int, v uint64) int {
- offset -= sovMembership(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *RaftAttributes) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if len(m.PeerUrls) > 0 {
- for _, s := range m.PeerUrls {
- l = len(s)
- n += 1 + l + sovMembership(uint64(l))
- }
- }
- if m.IsLearner {
- n += 2
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use ClusterVersionSetRequest.ProtoReflect.Descriptor instead.
+func (*ClusterVersionSetRequest) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{3}
}
-func (m *Attributes) Size() (n int) {
- if m == nil {
- return 0
+func (x *ClusterVersionSetRequest) GetVer() string {
+ if x != nil {
+ return x.Ver
}
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovMembership(uint64(l))
- }
- if len(m.ClientUrls) > 0 {
- for _, s := range m.ClientUrls {
- l = len(s)
- n += 1 + l + sovMembership(uint64(l))
- }
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return ""
}
-func (m *Member) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovMembership(uint64(m.ID))
- }
- if m.RaftAttributes != nil {
- l = m.RaftAttributes.Size()
- n += 1 + l + sovMembership(uint64(l))
- }
- if m.MemberAttributes != nil {
- l = m.MemberAttributes.Size()
- n += 1 + l + sovMembership(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+type ClusterMemberAttrSetRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Member_ID uint64 `protobuf:"varint,1,opt,name=member_ID,json=memberID,proto3" json:"member_ID,omitempty"`
+ MemberAttributes *Attributes `protobuf:"bytes,2,opt,name=member_attributes,json=memberAttributes,proto3" json:"member_attributes,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ClusterVersionSetRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Ver)
- if l > 0 {
- n += 1 + l + sovMembership(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+func (x *ClusterMemberAttrSetRequest) Reset() {
+ *x = ClusterMemberAttrSetRequest{}
+ mi := &file_membership_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *ClusterMemberAttrSetRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Member_ID != 0 {
- n += 1 + sovMembership(uint64(m.Member_ID))
- }
- if m.MemberAttributes != nil {
- l = m.MemberAttributes.Size()
- n += 1 + l + sovMembership(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+func (x *ClusterMemberAttrSetRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *DowngradeInfoSetRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Enabled {
- n += 2
- }
- l = len(m.Ver)
- if l > 0 {
- n += 1 + l + sovMembership(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
+func (*ClusterMemberAttrSetRequest) ProtoMessage() {}
-func sovMembership(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozMembership(x uint64) (n int) {
- return sovMembership(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *RaftAttributes) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: RaftAttributes: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: RaftAttributes: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PeerUrls", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.PeerUrls = append(m.PeerUrls, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.IsLearner = bool(v != 0)
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
+func (x *ClusterMemberAttrSetRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
+ return mi.MessageOf(x)
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+// Deprecated: Use ClusterMemberAttrSetRequest.ProtoReflect.Descriptor instead.
+func (*ClusterMemberAttrSetRequest) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{4}
}
-func (m *Attributes) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Attributes: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Attributes: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ClientUrls", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.ClientUrls = append(m.ClientUrls, string(dAtA[iNdEx:postIndex]))
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *ClusterMemberAttrSetRequest) GetMember_ID() uint64 {
+ if x != nil {
+ return x.Member_ID
}
- return nil
+ return 0
}
-func (m *Member) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Member: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Member: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field RaftAttributes", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.RaftAttributes == nil {
- m.RaftAttributes = &RaftAttributes{}
- }
- if err := m.RaftAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field MemberAttributes", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.MemberAttributes == nil {
- m.MemberAttributes = &Attributes{}
- }
- if err := m.MemberAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *ClusterMemberAttrSetRequest) GetMemberAttributes() *Attributes {
+ if x != nil {
+ return x.MemberAttributes
}
return nil
}
-func (m *ClusterVersionSetRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ClusterVersionSetRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ClusterVersionSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Ver = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+type DowngradeInfoSetRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+ Ver string `protobuf:"bytes,2,opt,name=ver,proto3" json:"ver,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ClusterMemberAttrSetRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ClusterMemberAttrSetRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ClusterMemberAttrSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Member_ID", wireType)
- }
- m.Member_ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Member_ID |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field MemberAttributes", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.MemberAttributes == nil {
- m.MemberAttributes = &Attributes{}
- }
- if err := m.MemberAttributes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *DowngradeInfoSetRequest) Reset() {
+ *x = DowngradeInfoSetRequest{}
+ mi := &file_membership_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *DowngradeInfoSetRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: DowngradeInfoSetRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: DowngradeInfoSetRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType)
- }
- var v int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- v |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- m.Enabled = bool(v != 0)
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Ver", wireType)
- }
- var stringLen uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- stringLen |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- intStringLen := int(stringLen)
- if intStringLen < 0 {
- return ErrInvalidLengthMembership
- }
- postIndex := iNdEx + intStringLen
- if postIndex < 0 {
- return ErrInvalidLengthMembership
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Ver = string(dAtA[iNdEx:postIndex])
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipMembership(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthMembership
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *DowngradeInfoSetRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func skipMembership(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowMembership
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthMembership
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupMembership
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthMembership
- }
- if depth == 0 {
- return iNdEx, nil
+
+func (*DowngradeInfoSetRequest) ProtoMessage() {}
+
+func (x *DowngradeInfoSetRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_membership_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
- return 0, io.ErrUnexpectedEOF
+ return mi.MessageOf(x)
}
+// Deprecated: Use DowngradeInfoSetRequest.ProtoReflect.Descriptor instead.
+func (*DowngradeInfoSetRequest) Descriptor() ([]byte, []int) {
+ return file_membership_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *DowngradeInfoSetRequest) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+func (x *DowngradeInfoSetRequest) GetVer() string {
+ if x != nil {
+ return x.Ver
+ }
+ return ""
+}
+
+var File_membership_proto protoreflect.FileDescriptor
+
+const file_membership_proto_rawDesc = "" +
+ "\n" +
+ "\x10membership.proto\x12\fmembershippb\x1a etcd/api/versionpb/version.proto\"U\n" +
+ "\x0eRaftAttributes\x12\x1b\n" +
+ "\tpeer_urls\x18\x01 \x03(\tR\bpeerUrls\x12\x1d\n" +
+ "\n" +
+ "is_learner\x18\x02 \x01(\bR\tisLearner:\a\x82\xb5\x18\x033.5\"J\n" +
+ "\n" +
+ "Attributes\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n" +
+ "\vclient_urls\x18\x02 \x03(\tR\n" +
+ "clientUrls:\a\x82\xb5\x18\x033.5\"\xaf\x01\n" +
+ "\x06Member\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x04R\x02ID\x12E\n" +
+ "\x0fraft_attributes\x18\x02 \x01(\v2\x1c.membershippb.RaftAttributesR\x0eraftAttributes\x12E\n" +
+ "\x11member_attributes\x18\x03 \x01(\v2\x18.membershippb.AttributesR\x10memberAttributes:\a\x82\xb5\x18\x033.5\"5\n" +
+ "\x18ClusterVersionSetRequest\x12\x10\n" +
+ "\x03ver\x18\x01 \x01(\tR\x03ver:\a\x82\xb5\x18\x033.5\"\x8a\x01\n" +
+ "\x1bClusterMemberAttrSetRequest\x12\x1b\n" +
+ "\tmember_ID\x18\x01 \x01(\x04R\bmemberID\x12E\n" +
+ "\x11member_attributes\x18\x02 \x01(\v2\x18.membershippb.AttributesR\x10memberAttributes:\a\x82\xb5\x18\x033.5\"N\n" +
+ "\x17DowngradeInfoSetRequest\x12\x18\n" +
+ "\aenabled\x18\x01 \x01(\bR\aenabled\x12\x10\n" +
+ "\x03ver\x18\x02 \x01(\tR\x03ver:\a\x82\xb5\x18\x033.5B%Z#go.etcd.io/etcd/api/v3/membershippbb\x06proto3"
+
var (
- ErrInvalidLengthMembership = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowMembership = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupMembership = fmt.Errorf("proto: unexpected end of group")
+ file_membership_proto_rawDescOnce sync.Once
+ file_membership_proto_rawDescData []byte
)
+
+func file_membership_proto_rawDescGZIP() []byte {
+ file_membership_proto_rawDescOnce.Do(func() {
+ file_membership_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_membership_proto_rawDesc), len(file_membership_proto_rawDesc)))
+ })
+ return file_membership_proto_rawDescData
+}
+
+var file_membership_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
+var file_membership_proto_goTypes = []any{
+ (*RaftAttributes)(nil), // 0: membershippb.RaftAttributes
+ (*Attributes)(nil), // 1: membershippb.Attributes
+ (*Member)(nil), // 2: membershippb.Member
+ (*ClusterVersionSetRequest)(nil), // 3: membershippb.ClusterVersionSetRequest
+ (*ClusterMemberAttrSetRequest)(nil), // 4: membershippb.ClusterMemberAttrSetRequest
+ (*DowngradeInfoSetRequest)(nil), // 5: membershippb.DowngradeInfoSetRequest
+}
+var file_membership_proto_depIdxs = []int32{
+ 0, // 0: membershippb.Member.raft_attributes:type_name -> membershippb.RaftAttributes
+ 1, // 1: membershippb.Member.member_attributes:type_name -> membershippb.Attributes
+ 1, // 2: membershippb.ClusterMemberAttrSetRequest.member_attributes:type_name -> membershippb.Attributes
+ 3, // [3:3] is the sub-list for method output_type
+ 3, // [3:3] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_membership_proto_init() }
+func file_membership_proto_init() {
+ if File_membership_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_membership_proto_rawDesc), len(file_membership_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 6,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_membership_proto_goTypes,
+ DependencyIndexes: file_membership_proto_depIdxs,
+ MessageInfos: file_membership_proto_msgTypes,
+ }.Build()
+ File_membership_proto = out.File
+ file_membership_proto_goTypes = nil
+ file_membership_proto_depIdxs = nil
+}
diff --git a/api/membershippb/membership.proto b/api/membershippb/membership.proto
index cbccfefccf45..93a632f01b83 100644
--- a/api/membershippb/membership.proto
+++ b/api/membershippb/membership.proto
@@ -1,16 +1,10 @@
syntax = "proto3";
package membershippb;
-import "gogoproto/gogo.proto";
import "etcd/api/versionpb/version.proto";
option go_package = "go.etcd.io/etcd/api/v3/membershippb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-
// RaftAttributes represents the raft related attributes of an etcd member.
message RaftAttributes {
option (versionpb.etcd_version_msg) = "3.5";
diff --git a/api/mvccpb/deprecated.go b/api/mvccpb/deprecated.go
new file mode 100644
index 000000000000..cc810ab45d41
--- /dev/null
+++ b/api/mvccpb/deprecated.go
@@ -0,0 +1,24 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 mvccpb
+
+const (
+ // PUT is an alias of Event_PUT
+ // Deprecated: use Event_PUT instead. Will be removed in v3.8.
+ PUT = Event_PUT
+ // DELETE is an alias of Permission_WRITE
+ // Deprecated: use Event_DELETE instead. Will be removed in v3.8.
+ DELETE = Event_DELETE
+)
diff --git a/api/mvccpb/extension.go b/api/mvccpb/extension.go
new file mode 100644
index 000000000000..616e1c69feb6
--- /dev/null
+++ b/api/mvccpb/extension.go
@@ -0,0 +1,25 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 mvccpb
+
+// IsCreate returns true if the event tells that the key is newly created.
+func (e *Event) IsCreate() bool {
+ return e.GetType() == PUT && e.GetKv().GetCreateRevision() == e.GetKv().GetModRevision()
+}
+
+// IsModify returns true if the event tells that a new value is put on existing key.
+func (e *Event) IsModify() bool {
+ return e.GetType() == PUT && e.GetKv().GetCreateRevision() != e.GetKv().GetModRevision()
+}
diff --git a/api/mvccpb/kv.pb.go b/api/mvccpb/kv.pb.go
index 2fed4242ccc6..73e72f966334 100644
--- a/api/mvccpb/kv.pb.go
+++ b/api/mvccpb/kv.pb.go
@@ -1,55 +1,75 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: kv.proto
package mvccpb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Event_EventType int32
const (
- PUT Event_EventType = 0
- DELETE Event_EventType = 1
+ Event_PUT Event_EventType = 0
+ Event_DELETE Event_EventType = 1
)
-var Event_EventType_name = map[int32]string{
- 0: "PUT",
- 1: "DELETE",
-}
+// Enum value maps for Event_EventType.
+var (
+ Event_EventType_name = map[int32]string{
+ 0: "PUT",
+ 1: "DELETE",
+ }
+ Event_EventType_value = map[string]int32{
+ "PUT": 0,
+ "DELETE": 1,
+ }
+)
-var Event_EventType_value = map[string]int32{
- "PUT": 0,
- "DELETE": 1,
+func (x Event_EventType) Enum() *Event_EventType {
+ p := new(Event_EventType)
+ *p = x
+ return p
}
func (x Event_EventType) String() string {
- return proto.EnumName(Event_EventType_name, int32(x))
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Event_EventType) Descriptor() protoreflect.EnumDescriptor {
+ return file_kv_proto_enumTypes[0].Descriptor()
+}
+
+func (Event_EventType) Type() protoreflect.EnumType {
+ return &file_kv_proto_enumTypes[0]
+}
+
+func (x Event_EventType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
}
+// Deprecated: Use Event_EventType.Descriptor instead.
func (Event_EventType) EnumDescriptor() ([]byte, []int) {
- return fileDescriptor_2216fe83c9c12408, []int{1, 0}
+ return file_kv_proto_rawDescGZIP(), []int{1, 0}
}
type KeyValue struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// key is the key in bytes. An empty key is not allowed.
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// create_revision is the revision of last creation on this key.
@@ -65,46 +85,85 @@ type KeyValue struct {
// lease is the ID of the lease that attached to key.
// When the attached lease expires, the key will be deleted.
// If lease is 0, then no lease is attached to the key.
- Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *KeyValue) Reset() { *m = KeyValue{} }
-func (m *KeyValue) String() string { return proto.CompactTextString(m) }
-func (*KeyValue) ProtoMessage() {}
-func (*KeyValue) Descriptor() ([]byte, []int) {
- return fileDescriptor_2216fe83c9c12408, []int{0}
+func (x *KeyValue) Reset() {
+ *x = KeyValue{}
+ mi := &file_kv_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *KeyValue) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
+
+func (x *KeyValue) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *KeyValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_KeyValue.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+
+func (*KeyValue) ProtoMessage() {}
+
+func (x *KeyValue) ProtoReflect() protoreflect.Message {
+ mi := &file_kv_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use KeyValue.ProtoReflect.Descriptor instead.
+func (*KeyValue) Descriptor() ([]byte, []int) {
+ return file_kv_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *KeyValue) GetKey() []byte {
+ if x != nil {
+ return x.Key
+ }
+ return nil
+}
+
+func (x *KeyValue) GetCreateRevision() int64 {
+ if x != nil {
+ return x.CreateRevision
}
+ return 0
}
-func (m *KeyValue) XXX_Merge(src proto.Message) {
- xxx_messageInfo_KeyValue.Merge(m, src)
+
+func (x *KeyValue) GetModRevision() int64 {
+ if x != nil {
+ return x.ModRevision
+ }
+ return 0
}
-func (m *KeyValue) XXX_Size() int {
- return m.Size()
+
+func (x *KeyValue) GetVersion() int64 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
}
-func (m *KeyValue) XXX_DiscardUnknown() {
- xxx_messageInfo_KeyValue.DiscardUnknown(m)
+
+func (x *KeyValue) GetValue() []byte {
+ if x != nil {
+ return x.Value
+ }
+ return nil
}
-var xxx_messageInfo_KeyValue proto.InternalMessageInfo
+func (x *KeyValue) GetLease() int64 {
+ if x != nil {
+ return x.Lease
+ }
+ return 0
+}
type Event struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// type is the kind of event. If type is a PUT, it indicates
// new data has been stored to the key. If type is a DELETE,
// it indicates the key was deleted.
@@ -116,685 +175,134 @@ type Event struct {
// its modification revision set to the revision of deletion.
Kv *KeyValue `protobuf:"bytes,2,opt,name=kv,proto3" json:"kv,omitempty"`
// prev_kv holds the key-value pair before the event happens.
- PrevKv *KeyValue `protobuf:"bytes,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ PrevKv *KeyValue `protobuf:"bytes,3,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Event) Reset() { *m = Event{} }
-func (m *Event) String() string { return proto.CompactTextString(m) }
-func (*Event) ProtoMessage() {}
-func (*Event) Descriptor() ([]byte, []int) {
- return fileDescriptor_2216fe83c9c12408, []int{1}
-}
-func (m *Event) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Event.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Event) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Event.Merge(m, src)
-}
-func (m *Event) XXX_Size() int {
- return m.Size()
-}
-func (m *Event) XXX_DiscardUnknown() {
- xxx_messageInfo_Event.DiscardUnknown(m)
+func (x *Event) Reset() {
+ *x = Event{}
+ mi := &file_kv_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-var xxx_messageInfo_Event proto.InternalMessageInfo
-
-func init() {
- proto.RegisterEnum("mvccpb.Event_EventType", Event_EventType_name, Event_EventType_value)
- proto.RegisterType((*KeyValue)(nil), "mvccpb.KeyValue")
- proto.RegisterType((*Event)(nil), "mvccpb.Event")
+func (x *Event) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func init() { proto.RegisterFile("kv.proto", fileDescriptor_2216fe83c9c12408) }
-
-var fileDescriptor_2216fe83c9c12408 = []byte{
- // 327 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xc1, 0x6a, 0xfa, 0x40,
- 0x10, 0xc6, 0xb3, 0x46, 0xa3, 0xff, 0x51, 0xfc, 0x87, 0x45, 0x68, 0x28, 0x34, 0xa4, 0x5e, 0x6a,
- 0x29, 0x24, 0xa0, 0x87, 0xde, 0x4b, 0x73, 0xb2, 0x87, 0x12, 0x6c, 0x0f, 0xbd, 0x48, 0x8c, 0x83,
- 0x84, 0xa8, 0x1b, 0x62, 0xba, 0x90, 0x37, 0xe9, 0xbd, 0xf7, 0x3e, 0x87, 0x47, 0x1f, 0xa1, 0xda,
- 0x17, 0x29, 0x3b, 0x5b, 0xed, 0xa5, 0x97, 0xdd, 0x99, 0xef, 0xfb, 0xb1, 0xf3, 0x0d, 0x0b, 0xad,
- 0x4c, 0xfa, 0x79, 0x21, 0x4a, 0xc1, 0xad, 0x95, 0x4c, 0x92, 0x7c, 0x76, 0xde, 0x5b, 0x88, 0x85,
- 0x20, 0x29, 0x50, 0x95, 0x76, 0xfb, 0x1f, 0x0c, 0x5a, 0x63, 0xac, 0x9e, 0xe3, 0xe5, 0x2b, 0x72,
- 0x1b, 0xcc, 0x0c, 0x2b, 0x87, 0x79, 0x6c, 0xd0, 0x89, 0x54, 0xc9, 0xaf, 0xe0, 0x7f, 0x52, 0x60,
- 0x5c, 0xe2, 0xb4, 0x40, 0x99, 0x6e, 0x52, 0xb1, 0x76, 0x6a, 0x1e, 0x1b, 0x98, 0x51, 0x57, 0xcb,
- 0xd1, 0x8f, 0xca, 0x2f, 0xa1, 0xb3, 0x12, 0xf3, 0x5f, 0xca, 0x24, 0xaa, 0xbd, 0x12, 0xf3, 0x13,
- 0xe2, 0x40, 0x53, 0x62, 0x41, 0x6e, 0x9d, 0xdc, 0x63, 0xcb, 0x7b, 0xd0, 0x90, 0x2a, 0x80, 0xd3,
- 0xa0, 0xc9, 0xba, 0x51, 0xea, 0x12, 0xe3, 0x0d, 0x3a, 0x16, 0xd1, 0xba, 0xe9, 0xbf, 0x33, 0x68,
- 0x84, 0x12, 0xd7, 0x25, 0xbf, 0x81, 0x7a, 0x59, 0xe5, 0x48, 0x71, 0xbb, 0xc3, 0x33, 0x5f, 0xef,
- 0xe9, 0x93, 0xa9, 0xcf, 0x49, 0x95, 0x63, 0x44, 0x10, 0xf7, 0xa0, 0x96, 0x49, 0xca, 0xde, 0x1e,
- 0xda, 0x47, 0xf4, 0xb8, 0x78, 0x54, 0xcb, 0x24, 0xbf, 0x86, 0x66, 0x5e, 0xa0, 0x9c, 0x66, 0x92,
- 0xc2, 0xff, 0x85, 0x59, 0x0a, 0x18, 0xcb, 0xbe, 0x07, 0xff, 0x4e, 0xef, 0xf3, 0x26, 0x98, 0x8f,
- 0x4f, 0x13, 0xdb, 0xe0, 0x00, 0xd6, 0x7d, 0xf8, 0x10, 0x4e, 0x42, 0x9b, 0xdd, 0xdd, 0x6e, 0xf7,
- 0xae, 0xb1, 0xdb, 0xbb, 0xc6, 0xf6, 0xe0, 0xb2, 0xdd, 0xc1, 0x65, 0x9f, 0x07, 0x97, 0xbd, 0x7d,
- 0xb9, 0xc6, 0xcb, 0xc5, 0x42, 0xf8, 0x58, 0x26, 0x73, 0x3f, 0x15, 0x81, 0xba, 0x83, 0x38, 0x4f,
- 0x03, 0x39, 0x0a, 0xf4, 0xac, 0x99, 0x45, 0xdf, 0x32, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x78,
- 0x06, 0x46, 0xf5, 0xc0, 0x01, 0x00, 0x00,
-}
+func (*Event) ProtoMessage() {}
-func (m *KeyValue) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *KeyValue) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *KeyValue) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Lease != 0 {
- i = encodeVarintKv(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x30
- }
- if len(m.Value) > 0 {
- i -= len(m.Value)
- copy(dAtA[i:], m.Value)
- i = encodeVarintKv(dAtA, i, uint64(len(m.Value)))
- i--
- dAtA[i] = 0x2a
- }
- if m.Version != 0 {
- i = encodeVarintKv(dAtA, i, uint64(m.Version))
- i--
- dAtA[i] = 0x20
- }
- if m.ModRevision != 0 {
- i = encodeVarintKv(dAtA, i, uint64(m.ModRevision))
- i--
- dAtA[i] = 0x18
- }
- if m.CreateRevision != 0 {
- i = encodeVarintKv(dAtA, i, uint64(m.CreateRevision))
- i--
- dAtA[i] = 0x10
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintKv(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *Event) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *Event) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Event) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.PrevKv != nil {
- {
- size, err := m.PrevKv.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintKv(dAtA, i, uint64(size))
+func (x *Event) ProtoReflect() protoreflect.Message {
+ mi := &file_kv_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x1a
+ return ms
}
- if m.Kv != nil {
- {
- size, err := m.Kv.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintKv(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.Type != 0 {
- i = encodeVarintKv(dAtA, i, uint64(m.Type))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func encodeVarintKv(dAtA []byte, offset int, v uint64) int {
- offset -= sovKv(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *KeyValue) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovKv(uint64(l))
- }
- if m.CreateRevision != 0 {
- n += 1 + sovKv(uint64(m.CreateRevision))
- }
- if m.ModRevision != 0 {
- n += 1 + sovKv(uint64(m.ModRevision))
- }
- if m.Version != 0 {
- n += 1 + sovKv(uint64(m.Version))
- }
- l = len(m.Value)
- if l > 0 {
- n += 1 + l + sovKv(uint64(l))
- }
- if m.Lease != 0 {
- n += 1 + sovKv(uint64(m.Lease))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use Event.ProtoReflect.Descriptor instead.
+func (*Event) Descriptor() ([]byte, []int) {
+ return file_kv_proto_rawDescGZIP(), []int{1}
}
-func (m *Event) Size() (n int) {
- if m == nil {
- return 0
+func (x *Event) GetType() Event_EventType {
+ if x != nil {
+ return x.Type
}
- var l int
- _ = l
- if m.Type != 0 {
- n += 1 + sovKv(uint64(m.Type))
- }
- if m.Kv != nil {
- l = m.Kv.Size()
- n += 1 + l + sovKv(uint64(l))
- }
- if m.PrevKv != nil {
- l = m.PrevKv.Size()
- n += 1 + l + sovKv(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return Event_PUT
}
-func sovKv(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozKv(x uint64) (n int) {
- return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *KeyValue) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: KeyValue: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: KeyValue: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthKv
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthKv
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
- }
- m.CreateRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.CreateRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
- }
- m.ModRevision = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ModRevision |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
- }
- m.Version = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Version |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 5:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthKv
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthKv
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
- if m.Value == nil {
- m.Value = []byte{}
- }
- iNdEx = postIndex
- case 6:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- m.Lease = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Lease |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipKv(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthKv
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Event) GetKv() *KeyValue {
+ if x != nil {
+ return x.Kv
}
return nil
}
-func (m *Event) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Event: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
- }
- m.Type = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Type |= Event_EventType(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthKv
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthKv
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Kv == nil {
- m.Kv = &KeyValue{}
- }
- if err := m.Kv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowKv
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthKv
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthKv
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.PrevKv == nil {
- m.PrevKv = &KeyValue{}
- }
- if err := m.PrevKv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipKv(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthKv
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Event) GetPrevKv() *KeyValue {
+ if x != nil {
+ return x.PrevKv
}
return nil
}
-func skipKv(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowKv
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowKv
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowKv
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthKv
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupKv
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthKv
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+
+var File_kv_proto protoreflect.FileDescriptor
+
+const file_kv_proto_rawDesc = "" +
+ "\n" +
+ "\bkv.proto\x12\x06mvccpb\"\xae\x01\n" +
+ "\bKeyValue\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\x12'\n" +
+ "\x0fcreate_revision\x18\x02 \x01(\x03R\x0ecreateRevision\x12!\n" +
+ "\fmod_revision\x18\x03 \x01(\x03R\vmodRevision\x12\x18\n" +
+ "\aversion\x18\x04 \x01(\x03R\aversion\x12\x14\n" +
+ "\x05value\x18\x05 \x01(\fR\x05value\x12\x14\n" +
+ "\x05lease\x18\x06 \x01(\x03R\x05lease\"\xa3\x01\n" +
+ "\x05Event\x12+\n" +
+ "\x04type\x18\x01 \x01(\x0e2\x17.mvccpb.Event.EventTypeR\x04type\x12 \n" +
+ "\x02kv\x18\x02 \x01(\v2\x10.mvccpb.KeyValueR\x02kv\x12)\n" +
+ "\aprev_kv\x18\x03 \x01(\v2\x10.mvccpb.KeyValueR\x06prevKv\" \n" +
+ "\tEventType\x12\a\n" +
+ "\x03PUT\x10\x00\x12\n" +
+ "\n" +
+ "\x06DELETE\x10\x01B\x1fZ\x1dgo.etcd.io/etcd/api/v3/mvccpbb\x06proto3"
var (
- ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowKv = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupKv = fmt.Errorf("proto: unexpected end of group")
+ file_kv_proto_rawDescOnce sync.Once
+ file_kv_proto_rawDescData []byte
)
+
+func file_kv_proto_rawDescGZIP() []byte {
+ file_kv_proto_rawDescOnce.Do(func() {
+ file_kv_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_kv_proto_rawDesc), len(file_kv_proto_rawDesc)))
+ })
+ return file_kv_proto_rawDescData
+}
+
+var file_kv_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_kv_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_kv_proto_goTypes = []any{
+ (Event_EventType)(0), // 0: mvccpb.Event.EventType
+ (*KeyValue)(nil), // 1: mvccpb.KeyValue
+ (*Event)(nil), // 2: mvccpb.Event
+}
+var file_kv_proto_depIdxs = []int32{
+ 0, // 0: mvccpb.Event.type:type_name -> mvccpb.Event.EventType
+ 1, // 1: mvccpb.Event.kv:type_name -> mvccpb.KeyValue
+ 1, // 2: mvccpb.Event.prev_kv:type_name -> mvccpb.KeyValue
+ 3, // [3:3] is the sub-list for method output_type
+ 3, // [3:3] is the sub-list for method input_type
+ 3, // [3:3] is the sub-list for extension type_name
+ 3, // [3:3] is the sub-list for extension extendee
+ 0, // [0:3] is the sub-list for field type_name
+}
+
+func init() { file_kv_proto_init() }
+func file_kv_proto_init() {
+ if File_kv_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_kv_proto_rawDesc), len(file_kv_proto_rawDesc)),
+ NumEnums: 1,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_kv_proto_goTypes,
+ DependencyIndexes: file_kv_proto_depIdxs,
+ EnumInfos: file_kv_proto_enumTypes,
+ MessageInfos: file_kv_proto_msgTypes,
+ }.Build()
+ File_kv_proto = out.File
+ file_kv_proto_goTypes = nil
+ file_kv_proto_depIdxs = nil
+}
diff --git a/api/mvccpb/kv.proto b/api/mvccpb/kv.proto
index a93479c69f74..6e035362f6e2 100644
--- a/api/mvccpb/kv.proto
+++ b/api/mvccpb/kv.proto
@@ -1,16 +1,8 @@
syntax = "proto3";
package mvccpb;
-import "gogoproto/gogo.proto";
-
option go_package = "go.etcd.io/etcd/api/v3/mvccpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-option (gogoproto.goproto_enum_prefix_all) = false;
-
message KeyValue {
// key is the key in bytes. An empty key is not allowed.
bytes key = 1;
diff --git a/api/version/version.go b/api/version/version.go
index 618f1f1b2053..ad23060722e8 100644
--- a/api/version/version.go
+++ b/api/version/version.go
@@ -26,7 +26,7 @@ import (
var (
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
MinClusterVersion = "3.0.0"
- Version = "3.6.10"
+ Version = "3.7.0"
APIVersion = "unknown"
// Git SHA Value will be set during build
@@ -43,6 +43,7 @@ var (
V3_5 = semver.Version{Major: 3, Minor: 5}
V3_6 = semver.Version{Major: 3, Minor: 6}
V3_7 = semver.Version{Major: 3, Minor: 7}
+ V3_8 = semver.Version{Major: 3, Minor: 8}
V4_0 = semver.Version{Major: 4, Minor: 0}
// AllVersions keeps all the versions in ascending order.
diff --git a/api/versionpb/version.pb.go b/api/versionpb/version.pb.go
index 71c74eb71929..8653baa7fbaa 100644
--- a/api/versionpb/version.pb.go
+++ b/api/versionpb/version.pb.go
@@ -1,91 +1,134 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: version.proto
package versionpb
import (
- fmt "fmt"
- math "math"
+ reflect "reflect"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
- proto "github.com/golang/protobuf/proto"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ descriptorpb "google.golang.org/protobuf/types/descriptorpb"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
-var E_EtcdVersionMsg = &proto.ExtensionDesc{
- ExtendedType: (*descriptor.MessageOptions)(nil),
- ExtensionType: (*string)(nil),
- Field: 50000,
- Name: "versionpb.etcd_version_msg",
- Tag: "bytes,50000,opt,name=etcd_version_msg",
- Filename: "version.proto",
+var file_version_proto_extTypes = []protoimpl.ExtensionInfo{
+ {
+ ExtendedType: (*descriptorpb.MessageOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50000,
+ Name: "versionpb.etcd_version_msg",
+ Tag: "bytes,50000,opt,name=etcd_version_msg",
+ Filename: "version.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50001,
+ Name: "versionpb.etcd_version_field",
+ Tag: "bytes,50001,opt,name=etcd_version_field",
+ Filename: "version.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.EnumOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50002,
+ Name: "versionpb.etcd_version_enum",
+ Tag: "bytes,50002,opt,name=etcd_version_enum",
+ Filename: "version.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.EnumValueOptions)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 50003,
+ Name: "versionpb.etcd_version_enum_value",
+ Tag: "bytes,50003,opt,name=etcd_version_enum_value",
+ Filename: "version.proto",
+ },
}
-var E_EtcdVersionField = &proto.ExtensionDesc{
- ExtendedType: (*descriptor.FieldOptions)(nil),
- ExtensionType: (*string)(nil),
- Field: 50001,
- Name: "versionpb.etcd_version_field",
- Tag: "bytes,50001,opt,name=etcd_version_field",
- Filename: "version.proto",
-}
+// Extension fields to descriptorpb.MessageOptions.
+var (
+ // optional string etcd_version_msg = 50000;
+ E_EtcdVersionMsg = &file_version_proto_extTypes[0]
+)
-var E_EtcdVersionEnum = &proto.ExtensionDesc{
- ExtendedType: (*descriptor.EnumOptions)(nil),
- ExtensionType: (*string)(nil),
- Field: 50002,
- Name: "versionpb.etcd_version_enum",
- Tag: "bytes,50002,opt,name=etcd_version_enum",
- Filename: "version.proto",
-}
+// Extension fields to descriptorpb.FieldOptions.
+var (
+ // optional string etcd_version_field = 50001;
+ E_EtcdVersionField = &file_version_proto_extTypes[1]
+)
-var E_EtcdVersionEnumValue = &proto.ExtensionDesc{
- ExtendedType: (*descriptor.EnumValueOptions)(nil),
- ExtensionType: (*string)(nil),
- Field: 50003,
- Name: "versionpb.etcd_version_enum_value",
- Tag: "bytes,50003,opt,name=etcd_version_enum_value",
- Filename: "version.proto",
-}
+// Extension fields to descriptorpb.EnumOptions.
+var (
+ // optional string etcd_version_enum = 50002;
+ E_EtcdVersionEnum = &file_version_proto_extTypes[2]
+)
-func init() {
- proto.RegisterExtension(E_EtcdVersionMsg)
- proto.RegisterExtension(E_EtcdVersionField)
- proto.RegisterExtension(E_EtcdVersionEnum)
- proto.RegisterExtension(E_EtcdVersionEnumValue)
-}
+// Extension fields to descriptorpb.EnumValueOptions.
+var (
+ // optional string etcd_version_enum_value = 50003;
+ E_EtcdVersionEnumValue = &file_version_proto_extTypes[3]
+)
+
+var File_version_proto protoreflect.FileDescriptor
-func init() { proto.RegisterFile("version.proto", fileDescriptor_7d2c07d79758f814) }
+const file_version_proto_rawDesc = "" +
+ "\n" +
+ "\rversion.proto\x12\tversionpb\x1a google/protobuf/descriptor.proto:N\n" +
+ "\x10etcd_version_msg\x12\x1f.google.protobuf.MessageOptions\x18І\x03 \x01(\tR\x0eetcdVersionMsg\x88\x01\x01:P\n" +
+ "\x12etcd_version_field\x12\x1d.google.protobuf.FieldOptions\x18ц\x03 \x01(\tR\x10etcdVersionField\x88\x01\x01:M\n" +
+ "\x11etcd_version_enum\x12\x1c.google.protobuf.EnumOptions\x18҆\x03 \x01(\tR\x0fetcdVersionEnum\x88\x01\x01:]\n" +
+ "\x17etcd_version_enum_value\x12!.google.protobuf.EnumValueOptions\x18ӆ\x03 \x01(\tR\x14etcdVersionEnumValue\x88\x01\x01B\"Z go.etcd.io/etcd/api/v3/versionpbb\x06proto3"
+
+var file_version_proto_goTypes = []any{
+ (*descriptorpb.MessageOptions)(nil), // 0: google.protobuf.MessageOptions
+ (*descriptorpb.FieldOptions)(nil), // 1: google.protobuf.FieldOptions
+ (*descriptorpb.EnumOptions)(nil), // 2: google.protobuf.EnumOptions
+ (*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions
+}
+var file_version_proto_depIdxs = []int32{
+ 0, // 0: versionpb.etcd_version_msg:extendee -> google.protobuf.MessageOptions
+ 1, // 1: versionpb.etcd_version_field:extendee -> google.protobuf.FieldOptions
+ 2, // 2: versionpb.etcd_version_enum:extendee -> google.protobuf.EnumOptions
+ 3, // 3: versionpb.etcd_version_enum_value:extendee -> google.protobuf.EnumValueOptions
+ 4, // [4:4] is the sub-list for method output_type
+ 4, // [4:4] is the sub-list for method input_type
+ 4, // [4:4] is the sub-list for extension type_name
+ 0, // [0:4] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
-var fileDescriptor_7d2c07d79758f814 = []byte{
- // 284 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0xd1, 0xc1, 0x4a, 0xc3, 0x30,
- 0x18, 0x07, 0x70, 0x83, 0x20, 0x2c, 0xa0, 0xce, 0x30, 0x50, 0x86, 0xd6, 0x7a, 0xf3, 0x94, 0x80,
- 0xbb, 0xed, 0x28, 0xe8, 0xad, 0x2a, 0x1e, 0x76, 0x10, 0xa4, 0xb4, 0x6b, 0x16, 0x02, 0x6d, 0xbf,
- 0xd0, 0xb4, 0x7d, 0x04, 0xd9, 0x23, 0xf8, 0x48, 0x1e, 0xa7, 0xbe, 0x80, 0xd4, 0x17, 0x91, 0xa4,
- 0xa9, 0xac, 0xd6, 0x53, 0xfb, 0x7d, 0xdf, 0xff, 0xff, 0xeb, 0xa1, 0x78, 0xbf, 0xe6, 0x85, 0x96,
- 0x90, 0x53, 0x55, 0x40, 0x09, 0x64, 0xe4, 0x46, 0x15, 0x4f, 0x27, 0x02, 0x04, 0xd8, 0x2d, 0x33,
- 0x6f, 0x6d, 0x60, 0xea, 0x0b, 0x00, 0x91, 0x72, 0x66, 0xa7, 0xb8, 0x5a, 0xb1, 0x84, 0xeb, 0x65,
- 0x21, 0x55, 0x09, 0x45, 0x9b, 0x98, 0xdf, 0xe1, 0x31, 0x2f, 0x97, 0x49, 0xe8, 0xa4, 0x30, 0xd3,
- 0x82, 0x9c, 0xd3, 0xb6, 0x46, 0xbb, 0x1a, 0x0d, 0xb8, 0xd6, 0x91, 0xe0, 0xf7, 0xaa, 0x94, 0x90,
- 0xeb, 0x93, 0xcd, 0xcb, 0xae, 0x8f, 0x2e, 0x47, 0x8f, 0x07, 0xa6, 0xba, 0x68, 0x9b, 0x81, 0x16,
- 0x6b, 0x84, 0xe6, 0x0f, 0x98, 0xf4, 0xbc, 0x95, 0xe4, 0x69, 0x42, 0xce, 0x06, 0xe2, 0xad, 0xd9,
- 0x77, 0xde, 0xbb, 0xf3, 0xc6, 0x5b, 0x9e, 0x0d, 0x18, 0x31, 0xc0, 0x47, 0x3d, 0x91, 0xe7, 0x55,
- 0x46, 0x4e, 0x07, 0xe0, 0x4d, 0x5e, 0x65, 0x9d, 0xf7, 0xe1, 0xbc, 0xc3, 0x2d, 0xcf, 0xdc, 0x0d,
- 0xf7, 0x8c, 0x8f, 0x07, 0x5c, 0x58, 0x47, 0x69, 0xc5, 0xc9, 0xc5, 0xbf, 0xe8, 0xc2, 0xdc, 0x3a,
- 0xf9, 0xd3, 0xc9, 0x93, 0x3f, 0xb2, 0x0d, 0xad, 0x11, 0xba, 0xbe, 0x7a, 0x6b, 0x3c, 0xb4, 0x69,
- 0x3c, 0xf4, 0xd5, 0x78, 0xe8, 0xf5, 0xdb, 0xdb, 0x79, 0xf2, 0x05, 0x50, 0x93, 0xa6, 0x12, 0x98,
- 0x79, 0xb2, 0x48, 0x49, 0x56, 0xcf, 0xd8, 0xef, 0xbf, 0x8b, 0xf7, 0xec, 0xf7, 0x66, 0x3f, 0x01,
- 0x00, 0x00, 0xff, 0xff, 0xe8, 0x02, 0x15, 0xc0, 0xde, 0x01, 0x00, 0x00,
+func init() { file_version_proto_init() }
+func file_version_proto_init() {
+ if File_version_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_version_proto_rawDesc), len(file_version_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 0,
+ NumExtensions: 4,
+ NumServices: 0,
+ },
+ GoTypes: file_version_proto_goTypes,
+ DependencyIndexes: file_version_proto_depIdxs,
+ ExtensionInfos: file_version_proto_extTypes,
+ }.Build()
+ File_version_proto = out.File
+ file_version_proto_goTypes = nil
+ file_version_proto_depIdxs = nil
}
diff --git a/api/versionpb/version.proto b/api/versionpb/version.proto
index c81b2f58a44d..1ee99fadb19f 100644
--- a/api/versionpb/version.proto
+++ b/api/versionpb/version.proto
@@ -1,14 +1,10 @@
syntax = "proto3";
package versionpb;
-import "gogoproto/gogo.proto";
import "google/protobuf/descriptor.proto";
option go_package = "go.etcd.io/etcd/api/v3/versionpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-
// Indicates etcd version that introduced the message, used to determine minimal etcd version required to interpret wal that includes this message.
extend google.protobuf.MessageOptions {
optional string etcd_version_msg = 50000;
diff --git a/bill-of-materials.json b/bill-of-materials.json
index 6519d652b1fa..a507db65e1dc 100644
--- a/bill-of-materials.json
+++ b/bill-of-materials.json
@@ -10,6 +10,15 @@
},
{
"project": "github.com/anishathalye/porcupine",
+ "licenses": [
+ {
+ "type": "MIT License",
+ "confidence": 0.96875
+ }
+ ]
+ },
+ {
+ "project": "github.com/antithesishq/antithesis-sdk-go",
"licenses": [
{
"type": "MIT License",
@@ -36,7 +45,7 @@
]
},
{
- "project": "github.com/cenkalti/backoff/v4",
+ "project": "github.com/cenkalti/backoff/v5",
"licenses": [
{
"type": "MIT License",
@@ -62,6 +71,24 @@
}
]
},
+ {
+ "project": "github.com/clipperhouse/displaywidth",
+ "licenses": [
+ {
+ "type": "MIT License",
+ "confidence": 1
+ }
+ ]
+ },
+ {
+ "project": "github.com/clipperhouse/uax29/v2/graphemes",
+ "licenses": [
+ {
+ "type": "MIT License",
+ "confidence": 1
+ }
+ ]
+ },
{
"project": "github.com/coreos/go-semver/semver",
"licenses": [
@@ -134,15 +161,6 @@
}
]
},
- {
- "project": "github.com/gogo/protobuf",
- "licenses": [
- {
- "type": "BSD 3-clause \"New\" or \"Revised\" License",
- "confidence": 0.9163346613545816
- }
- ]
- },
{
"project": "github.com/golang-jwt/jwt/v5",
"licenses": [
@@ -152,15 +170,6 @@
}
]
},
- {
- "project": "github.com/golang/groupcache/lru",
- "licenses": [
- {
- "type": "Apache License 2.0",
- "confidence": 0.9966703662597114
- }
- ]
- },
{
"project": "github.com/golang/protobuf",
"licenses": [
@@ -170,15 +179,6 @@
}
]
},
- {
- "project": "github.com/google/btree",
- "licenses": [
- {
- "type": "Apache License 2.0",
- "confidence": 1
- }
- ]
- },
{
"project": "github.com/google/go-cmp/cmp",
"licenses": [
@@ -206,15 +206,6 @@
}
]
},
- {
- "project": "github.com/grpc-ecosystem/go-grpc-middleware",
- "licenses": [
- {
- "type": "Apache License 2.0",
- "confidence": 1
- }
- ]
- },
{
"project": "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus",
"licenses": [
@@ -261,25 +252,25 @@
]
},
{
- "project": "github.com/klauspost/compress",
+ "project": "github.com/mattn/go-colorable",
"licenses": [
{
- "type": "Apache License 2.0",
- "confidence": 0.9376299376299376
+ "type": "MIT License",
+ "confidence": 1
}
]
},
{
- "project": "github.com/klauspost/compress/internal/snapref",
+ "project": "github.com/mattn/go-isatty",
"licenses": [
{
- "type": "BSD 3-clause \"New\" or \"Revised\" License",
- "confidence": 0.9663865546218487
+ "type": "MIT License",
+ "confidence": 0.9587628865979382
}
]
},
{
- "project": "github.com/klauspost/compress/zstd/internal/xxhash",
+ "project": "github.com/mattn/go-runewidth",
"licenses": [
{
"type": "MIT License",
@@ -288,25 +279,25 @@
]
},
{
- "project": "github.com/mattn/go-colorable",
+ "project": "github.com/munnerz/goautoneg",
"licenses": [
{
- "type": "MIT License",
- "confidence": 1
+ "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "confidence": 0.9794238683127572
}
]
},
{
- "project": "github.com/mattn/go-isatty",
+ "project": "github.com/olekukonko/cat",
"licenses": [
{
"type": "MIT License",
- "confidence": 0.9587628865979382
+ "confidence": 1
}
]
},
{
- "project": "github.com/mattn/go-runewidth",
+ "project": "github.com/olekukonko/errors",
"licenses": [
{
"type": "MIT License",
@@ -315,11 +306,11 @@
]
},
{
- "project": "github.com/munnerz/goautoneg",
+ "project": "github.com/olekukonko/ll",
"licenses": [
{
- "type": "BSD 3-clause \"New\" or \"Revised\" License",
- "confidence": 0.9794238683127572
+ "type": "MIT License",
+ "confidence": 1
}
]
},
@@ -386,15 +377,6 @@
}
]
},
- {
- "project": "github.com/rivo/uniseg",
- "licenses": [
- {
- "type": "MIT License",
- "confidence": 1
- }
- ]
- },
{
"project": "github.com/sirupsen/logrus",
"licenses": [
@@ -472,7 +454,16 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
+ }
+ ]
+ },
+ {
+ "project": "go.etcd.io/etcd/cache/v3",
+ "licenses": [
+ {
+ "type": "Apache License 2.0",
+ "confidence": 0.9988925802879292
}
]
},
@@ -481,7 +472,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -490,7 +481,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -499,7 +490,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -508,7 +499,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -517,7 +508,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -526,7 +517,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -535,7 +526,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -544,7 +535,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9988925802879292
}
]
},
@@ -580,7 +571,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9647812166488794
}
]
},
@@ -598,7 +589,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9647812166488794
}
]
},
@@ -607,7 +598,7 @@
"licenses": [
{
"type": "Apache License 2.0",
- "confidence": 1
+ "confidence": 0.9647812166488794
}
]
},
@@ -665,6 +656,19 @@
}
]
},
+ {
+ "project": "go.yaml.in/yaml/v2",
+ "licenses": [
+ {
+ "type": "Apache License 2.0",
+ "confidence": 1
+ },
+ {
+ "type": "MIT License",
+ "confidence": 0.8975609756097561
+ }
+ ]
+ },
{
"project": "golang.org/x/crypto",
"licenses": [
@@ -674,6 +678,15 @@
}
]
},
+ {
+ "project": "golang.org/x/mod/semver",
+ "licenses": [
+ {
+ "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "confidence": 0.9663865546218487
+ }
+ ]
+ },
{
"project": "golang.org/x/net",
"licenses": [
@@ -693,7 +706,7 @@
]
},
{
- "project": "golang.org/x/sys/unix",
+ "project": "golang.org/x/sys",
"licenses": [
{
"type": "BSD 3-clause \"New\" or \"Revised\" License",
@@ -719,6 +732,15 @@
}
]
},
+ {
+ "project": "golang.org/x/tools",
+ "licenses": [
+ {
+ "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "confidence": 0.9663865546218487
+ }
+ ]
+ },
{
"project": "google.golang.org/genproto/googleapis/api",
"licenses": [
@@ -774,37 +796,42 @@
]
},
{
- "project": "sigs.k8s.io/json",
+ "project": "k8s.io/utils/internal/third_party/forked/golang/golang-lru",
"licenses": [
{
- "type": "Apache License 2.0",
- "confidence": 0.9617021276595744
+ "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "confidence": 0.9663865546218487
}
]
},
{
- "project": "sigs.k8s.io/yaml",
+ "project": "k8s.io/utils/lru",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
- },
+ }
+ ]
+ },
+ {
+ "project": "k8s.io/utils/third_party/forked/golang/btree",
+ "licenses": [
{
- "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "type": "Apache License 2.0",
"confidence": 1
}
]
},
{
- "project": "sigs.k8s.io/yaml/goyaml.v2",
+ "project": "sigs.k8s.io/yaml",
"licenses": [
{
"type": "Apache License 2.0",
"confidence": 1
},
{
- "type": "MIT License",
- "confidence": 0.8975609756097561
+ "type": "BSD 3-clause \"New\" or \"Revised\" License",
+ "confidence": 1
}
]
}
diff --git a/cache/LICENSE b/cache/LICENSE
new file mode 100644
index 000000000000..52fb9c29f2e2
--- /dev/null
+++ b/cache/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020 The etcd Authors
+
+ 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/cache/OWNERS b/cache/OWNERS
new file mode 100644
index 000000000000..87a2e98b310e
--- /dev/null
+++ b/cache/OWNERS
@@ -0,0 +1,4 @@
+# See the OWNERS docs at https://go.k8s.io/owners
+
+labels:
+ - area/cache
diff --git a/cache/README.md b/cache/README.md
new file mode 100644
index 000000000000..5a66c5855c96
--- /dev/null
+++ b/cache/README.md
@@ -0,0 +1,5 @@
+# etcd cache
+
+Experimental etcd client cache library.
+
+**Note:** gRPC proxy is not supported. The cache relies on `RequestProgress` RPCs, which the gRPC proxy does not forward.
diff --git a/cache/cache.go b/cache/cache.go
new file mode 100644
index 000000000000..783e15309376
--- /dev/null
+++ b/cache/cache.go
@@ -0,0 +1,518 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+var (
+ // Returned when an option combination isn’t yet handled by the cache (e.g. WithPrevKV for Watch(), WithCountOnly for Get()).
+ ErrUnsupportedRequest = errors.New("cache: unsupported request parameters")
+ // Returned when the requested key or key‑range is invalid (empty or reversed) or lies outside c.prefix.
+ ErrKeyRangeInvalid = errors.New("cache: invalid or out‑of‑range key range")
+ // Returned when the cache timed out waiting for the requested revision
+ ErrCacheTimeout = errors.New("cache: timed out waiting for revision")
+)
+
+// Cache buffers a single etcd Watch for a given key‐prefix and fan‑outs local watchers.
+//
+// Note: gRPC proxy is not supported. Cache relies on RequestProgress RPCs,
+// which the gRPC proxy does not forward.
+type Cache struct {
+ prefix string // prefix is the key-prefix this shard is responsible for ("" = root).
+ cfg Config // immutable runtime configuration
+ watcher clientv3.Watcher
+ kv clientv3.KV
+ demux *demux // demux fans incoming events out to active watchers and manages resync.
+ store *store // last‑observed snapshot
+ ready *ready
+ stop context.CancelFunc
+ waitGroup sync.WaitGroup
+ internalCtx context.Context
+ progressRequestor progressRequestor
+ clock Clock
+}
+
+// New builds a cache shard that watches only the requested prefix.
+// For the root cache pass "".
+//
+// Note: gRPC proxy is not supported. Cache relies on RequestProgress RPCs,
+// which the gRPC proxy does not forward.
+func New(client *clientv3.Client, prefix string, opts ...Option) (*Cache, error) {
+ cfg := defaultConfig()
+ for _, opt := range opts {
+ opt(&cfg)
+ }
+ return newCache(client, prefix, cfg, realClock{})
+}
+
+func newCache(client *clientv3.Client, prefix string, cfg Config, clock Clock) (*Cache, error) {
+ if cfg.HistoryWindowSize <= 0 {
+ return nil, fmt.Errorf("invalid HistoryWindowSize %d (must be > 0)", cfg.HistoryWindowSize)
+ }
+ if cfg.BTreeDegree < 2 {
+ return nil, fmt.Errorf("invalid BTreeDegree %d (must be >= 2)", cfg.BTreeDegree)
+ }
+
+ internalCtx, cancel := context.WithCancel(context.Background())
+
+ cache := &Cache{
+ prefix: prefix,
+ cfg: cfg,
+ watcher: client.Watcher,
+ kv: client.KV,
+ store: newStore(cfg.BTreeDegree, cfg.HistoryWindowSize),
+ ready: newReady(),
+ stop: cancel,
+ internalCtx: internalCtx,
+ progressRequestor: newConditionalProgressRequestor(client.Watcher, clock, cfg.ProgressRequestInterval),
+ clock: clock,
+ }
+
+ cache.demux = NewDemux(internalCtx, &cache.waitGroup, cfg.HistoryWindowSize, cfg.ResyncInterval)
+
+ cache.waitGroup.Add(2)
+ go func() {
+ defer cache.waitGroup.Done()
+ cache.getWatchLoop()
+ }()
+ go func() {
+ defer cache.waitGroup.Done()
+ cache.progressRequestor.run(internalCtx)
+ }()
+
+ return cache, nil
+}
+
+func (c *Cache) RequestProgress(_ context.Context) error {
+ c.demux.BroadcastProgress()
+ return nil
+}
+
+// Watch registers a cache-backed watcher for a given key or prefix.
+// It returns a WatchChan that streams WatchResponses containing events.
+func (c *Cache) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
+ if err := c.WaitReady(ctx); err != nil {
+ emptyWatchChan := make(chan clientv3.WatchResponse)
+ close(emptyWatchChan)
+ return emptyWatchChan
+ }
+
+ op := clientv3.OpWatch(key, opts...)
+ startRev := op.Rev()
+
+ pred, err := c.validateWatch(key, op)
+ if err != nil {
+ ch := make(chan clientv3.WatchResponse, 1)
+ ch <- clientv3.WatchResponse{
+ Header: &pb.ResponseHeader{},
+ Canceled: true,
+ CancelReason: err.Error(),
+ }
+ close(ch)
+ return ch
+ }
+
+ w := newWatcher(c.cfg.PerWatcherBufferSize, pred)
+ c.demux.Register(w, startRev)
+
+ responseChan := make(chan clientv3.WatchResponse)
+ c.waitGroup.Add(1)
+ go func() {
+ defer c.waitGroup.Done()
+ defer close(responseChan)
+ defer c.demux.Unregister(w)
+
+ var progressTimer Timer
+ var progressChan <-chan time.Time
+ if op.IsProgressNotify() {
+ progressTimer = c.clock.NewTimer(c.cfg.ProgressNotifyInterval)
+ defer progressTimer.Stop()
+ progressChan = progressTimer.Chan()
+ }
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-c.internalCtx.Done():
+ return
+ case <-progressChan:
+ c.demux.BroadcastProgressTo(w)
+ progressTimer.Reset(c.cfg.ProgressNotifyInterval)
+ case resp, ok := <-w.respCh:
+ if !ok {
+ if w.cancelResp != nil {
+ select {
+ case <-ctx.Done():
+ case <-c.internalCtx.Done():
+ case responseChan <- *w.cancelResp:
+ }
+ }
+ return
+ }
+ select {
+ case <-ctx.Done():
+ return
+ case <-c.internalCtx.Done():
+ return
+ case responseChan <- resp:
+ if progressTimer != nil {
+ progressTimer.Reset(c.cfg.ProgressNotifyInterval)
+ }
+ }
+ }
+ }
+ }()
+ return responseChan
+}
+
+func (c *Cache) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
+ if c.store.LatestRev() == 0 {
+ if err := c.WaitReady(ctx); err != nil {
+ return nil, err
+ }
+ }
+ op := clientv3.OpGet(key, opts...)
+
+ if _, err := c.validateGet(key, op); err != nil {
+ return nil, err
+ }
+
+ startKey := []byte(key)
+ endKey := op.RangeBytes()
+ requestedRev := op.Rev()
+
+ if !op.IsSerializable() {
+ serverRev, err := c.serverRevision(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if requestedRev > serverRev {
+ return nil, rpctypes.ErrFutureRev
+ }
+ if err = c.waitTillRevision(ctx, serverRev); err != nil {
+ return nil, err
+ }
+ }
+
+ kvs, latestRev, err := c.store.Get(startKey, endKey, requestedRev)
+ if err != nil {
+ return nil, err
+ }
+
+ return &clientv3.GetResponse{
+ Header: &pb.ResponseHeader{Revision: latestRev},
+ Kvs: kvs,
+ Count: int64(len(kvs)),
+ }, nil
+}
+
+// Ready returns true if the snapshot has been loaded and the first watch has been confirmed.
+func (c *Cache) Ready() bool {
+ return c.ready.Ready()
+}
+
+// WaitReady blocks until the cache is ready or the ctx is cancelled.
+func (c *Cache) WaitReady(ctx context.Context) error {
+ return c.ready.WaitReady(ctx)
+}
+
+func (c *Cache) WaitForRevision(ctx context.Context, rev int64) error {
+ for {
+ if c.store.LatestRev() >= rev {
+ return nil
+ }
+ select {
+ case <-time.After(10 * time.Millisecond):
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
+
+func (c *Cache) serverRevision(ctx context.Context) (int64, error) {
+ key := c.prefix
+ if key == "" {
+ key = "/"
+ }
+ resp, err := c.kv.Get(ctx, key, clientv3.WithLimit(1), clientv3.WithCountOnly())
+ if err != nil {
+ return 0, err
+ }
+ return resp.Header.Revision, nil
+}
+
+func (c *Cache) waitTillRevision(ctx context.Context, rev int64) error {
+ if c.store.LatestRev() >= rev {
+ return nil
+ }
+
+ c.progressRequestor.add()
+ defer c.progressRequestor.remove()
+
+ startTime := time.Now()
+
+ timeoutCh := time.After(c.cfg.WaitTimeout)
+ go func() {
+ select {
+ case <-timeoutCh:
+ c.store.revCond.Broadcast()
+ case <-ctx.Done():
+ c.store.revCond.Broadcast()
+ }
+ }()
+
+ c.store.mu.RLock()
+ defer c.store.mu.RUnlock()
+
+ for c.store.latest.rev < rev {
+ if time.Since(startTime) >= c.cfg.WaitTimeout {
+ return ErrCacheTimeout
+ }
+
+ if ctx.Err() != nil {
+ return ctx.Err()
+ }
+
+ c.store.revCond.Wait()
+ }
+
+ return nil
+}
+
+// Close cancels the private context and blocks until all goroutines return.
+func (c *Cache) Close() {
+ c.stop()
+ c.waitGroup.Wait()
+}
+
+func (c *Cache) getWatchLoop() {
+ cfg := defaultConfig()
+ ctx := c.internalCtx
+ backoff := cfg.InitialBackoff
+ for {
+ if err := ctx.Err(); err != nil {
+ return
+ }
+ if err := c.getWatch(); err != nil {
+ fmt.Printf("getWatch failed, will retry after %v: %v\n", backoff, err)
+ }
+ select {
+ case <-ctx.Done():
+ return
+ case <-time.After(backoff):
+ }
+ }
+}
+
+func (c *Cache) getWatch() error {
+ getResp, err := c.get(c.internalCtx)
+ if err != nil {
+ return err
+ }
+ return c.watch(getResp.Header.Revision + 1)
+}
+
+func (c *Cache) get(ctx context.Context) (*clientv3.GetResponse, error) {
+ resp, err := c.kv.Get(ctx, c.prefix, clientv3.WithPrefix())
+ if err != nil {
+ return nil, err
+ }
+ c.store.Restore(resp.Kvs, resp.Header.Revision)
+ return resp, nil
+}
+
+func (c *Cache) watch(rev int64) error {
+ readyOnce := sync.Once{}
+ for {
+ storeW := newWatcher(c.cfg.PerWatcherBufferSize, nil)
+ c.demux.Register(storeW, rev)
+ applyErr := make(chan error, 1)
+ c.waitGroup.Add(1)
+ go func() {
+ defer c.waitGroup.Done()
+ if err := c.applyStorage(storeW); err != nil {
+ applyErr <- err
+ }
+ close(applyErr)
+ }()
+
+ err := c.watchEvents(rev, applyErr, &readyOnce)
+ c.demux.Unregister(storeW)
+
+ if err != nil {
+ return err
+ }
+ }
+}
+
+func (c *Cache) applyStorage(storeW *watcher) error {
+ for {
+ select {
+ case <-c.internalCtx.Done():
+ return nil
+ case resp, ok := <-storeW.respCh:
+ if !ok {
+ return nil
+ }
+ if err := c.store.Apply(resp); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (c *Cache) watchEvents(rev int64, applyErr <-chan error, readyOnce *sync.Once) error {
+ watchCh := c.watcher.Watch(
+ c.internalCtx,
+ c.prefix,
+ clientv3.WithPrefix(),
+ clientv3.WithRev(rev),
+ clientv3.WithProgressNotify(),
+ clientv3.WithCreatedNotify(),
+ )
+ for {
+ select {
+ case <-c.internalCtx.Done():
+ return c.internalCtx.Err()
+ case resp, ok := <-watchCh:
+ if !ok {
+ return nil
+ }
+ readyOnce.Do(func() {
+ c.demux.Init(rev)
+ c.ready.Set()
+ })
+ if err := resp.Err(); err != nil {
+ c.ready.Reset()
+ return err
+ }
+ err := c.demux.Broadcast(resp)
+ if err != nil {
+ c.ready.Reset()
+ return err
+ }
+ case err := <-applyErr:
+ c.ready.Reset()
+ return err
+ }
+ }
+}
+
+func (c *Cache) validateWatch(key string, op clientv3.Op) (pred KeyPredicate, err error) {
+ switch {
+ case op.IsPrevKV():
+ return nil, fmt.Errorf("%w: PrevKV not supported", ErrUnsupportedRequest)
+ case op.IsFragment():
+ return nil, fmt.Errorf("%w: Fragment not supported", ErrUnsupportedRequest)
+ case op.IsCreatedNotify():
+ return nil, fmt.Errorf("%w: CreatedNotify not supported", ErrUnsupportedRequest)
+ case op.IsFilterPut():
+ return nil, fmt.Errorf("%w: FilterPut not supported", ErrUnsupportedRequest)
+ case op.IsFilterDelete():
+ return nil, fmt.Errorf("%w: FilterDelete not supported", ErrUnsupportedRequest)
+ }
+
+ startKey := []byte(key)
+ endKey := op.RangeBytes() // nil = single key, {0}=FromKey, else explicit range
+
+ if err := c.validateRange(startKey, endKey); err != nil {
+ return nil, err
+ }
+ return KeyPredForRange(startKey, endKey), nil
+}
+
+func (c *Cache) validateGet(key string, op clientv3.Op) (KeyPredicate, error) {
+ switch {
+ case op.IsCountOnly():
+ return nil, fmt.Errorf("%w: CountOnly not supported", ErrUnsupportedRequest)
+ case op.IsPrevKV():
+ return nil, fmt.Errorf("%w: PrevKV not supported", ErrUnsupportedRequest)
+ case op.IsSortSet():
+ return nil, fmt.Errorf("%w: SortSet not supported", ErrUnsupportedRequest)
+ case op.Limit() != 0:
+ return nil, fmt.Errorf("%w: Limit(%d) not supported", ErrUnsupportedRequest, op.Limit())
+ case op.MinModRev() != 0:
+ return nil, fmt.Errorf("%w: MinModRev(%d) not supported", ErrUnsupportedRequest, op.MinModRev())
+ case op.MaxModRev() != 0:
+ return nil, fmt.Errorf("%w: MaxModRev(%d) not supported", ErrUnsupportedRequest, op.MaxModRev())
+ case op.MinCreateRev() != 0:
+ return nil, fmt.Errorf("%w: MinCreateRev(%d) not supported", ErrUnsupportedRequest, op.MinCreateRev())
+ case op.MaxCreateRev() != 0:
+ return nil, fmt.Errorf("%w: MaxCreateRev(%d) not supported", ErrUnsupportedRequest, op.MaxCreateRev())
+ }
+
+ startKey := []byte(key)
+ endKey := op.RangeBytes()
+
+ if err := c.validateRange(startKey, endKey); err != nil {
+ return nil, err
+ }
+
+ return KeyPredForRange(startKey, endKey), nil
+}
+
+func (c *Cache) validateRange(startKey, endKey []byte) error {
+ prefixStart := []byte(c.prefix)
+ prefixEnd := []byte(clientv3.GetPrefixRangeEnd(c.prefix))
+
+ isSingleKey := len(endKey) == 0
+ isFromKey := len(endKey) == 1 && endKey[0] == 0
+
+ switch {
+ case isSingleKey:
+ if c.prefix == "" {
+ return nil
+ }
+ if bytes.Compare(startKey, prefixStart) < 0 || bytes.Compare(startKey, prefixEnd) >= 0 {
+ return ErrKeyRangeInvalid
+ }
+ return nil
+
+ case isFromKey:
+ if c.prefix != "" {
+ return ErrKeyRangeInvalid
+ }
+ return nil
+
+ default:
+ if bytes.Compare(endKey, startKey) <= 0 {
+ return ErrKeyRangeInvalid
+ }
+ if c.prefix == "" {
+ return nil
+ }
+ if bytes.Compare(startKey, prefixStart) < 0 || bytes.Compare(endKey, prefixEnd) > 0 {
+ return ErrKeyRangeInvalid
+ }
+ return nil
+ }
+}
+
+// WaitForNextResync blocks until the next resync loop iteration is complete.
+func (c *Cache) WaitForNextResync(ctx context.Context) error {
+ return c.demux.WaitForNextResync(ctx)
+}
diff --git a/cache/cache_test.go b/cache/cache_test.go
new file mode 100644
index 000000000000..7725fd15e1df
--- /dev/null
+++ b/cache/cache_test.go
@@ -0,0 +1,1003 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+ "testing"
+ "testing/synctest"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+func TestCacheWatchAtomicOrderedDelivery(t *testing.T) {
+ tests := []struct {
+ name string
+ sentBatches [][]*clientv3.Event
+ wantBatch []*clientv3.Event
+ }{
+ {
+ name: "single_event",
+ sentBatches: [][]*clientv3.Event{
+ {event(mvccpb.Event_PUT, "/a", 5)},
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 5),
+ },
+ },
+ {
+ name: "same_revision_batch",
+ sentBatches: [][]*clientv3.Event{
+ {
+ event(mvccpb.Event_PUT, "/a", 10),
+ event(mvccpb.Event_PUT, "/b", 10),
+ },
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 10),
+ event(mvccpb.Event_PUT, "/b", 10),
+ },
+ },
+ {
+ name: "mixed_revisions_in_single_response",
+ sentBatches: [][]*clientv3.Event{
+ {
+ event(mvccpb.Event_PUT, "/a", 11),
+ event(mvccpb.Event_PUT, "/b", 11),
+ event(mvccpb.Event_PUT, "/c", 12),
+ },
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 11),
+ event(mvccpb.Event_PUT, "/b", 11),
+ event(mvccpb.Event_PUT, "/c", 12),
+ },
+ },
+ {
+ name: "mixed_event_types_same_revision",
+ sentBatches: [][]*clientv3.Event{
+ {
+ event(mvccpb.Event_PUT, "/x", 5),
+ event(mvccpb.Event_PUT, "/y", 6),
+ event(mvccpb.Event_DELETE, "/x", 6),
+ },
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/x", 5),
+ event(mvccpb.Event_PUT, "/y", 6),
+ event(mvccpb.Event_DELETE, "/x", 6),
+ },
+ },
+ {
+ name: "all_events_in_one_response",
+ sentBatches: [][]*clientv3.Event{
+ {
+ event(mvccpb.Event_PUT, "/a", 2),
+ event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4),
+ event(mvccpb.Event_PUT, "/e", 4),
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6),
+ event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7),
+ event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 2),
+ event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4),
+ event(mvccpb.Event_PUT, "/e", 4),
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6),
+ event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7),
+ event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ {
+ name: "one_revision_group_per_response",
+ sentBatches: [][]*clientv3.Event{
+ {event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2)},
+ {event(mvccpb.Event_PUT, "/c", 3)},
+ {event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4)},
+ {event(mvccpb.Event_PUT, "/f", 5)},
+ {event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6)},
+ {event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7)},
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 2),
+ event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4),
+ event(mvccpb.Event_PUT, "/e", 4),
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6),
+ event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7),
+ event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ {
+ name: "two_revision_groups_per_response",
+ sentBatches: [][]*clientv3.Event{
+ {event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2), event(mvccpb.Event_PUT, "/c", 3)},
+ {event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4), event(mvccpb.Event_PUT, "/f", 5)},
+ {event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6)},
+ {event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7)},
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 2),
+ event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4),
+ event(mvccpb.Event_PUT, "/e", 4),
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6),
+ event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7),
+ event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ {
+ name: "three_revision_groups_per_response",
+ sentBatches: [][]*clientv3.Event{
+ {
+ event(mvccpb.Event_PUT, "/a", 2), event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4), event(mvccpb.Event_PUT, "/e", 4),
+ },
+ {
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6), event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7), event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ wantBatch: []*clientv3.Event{
+ event(mvccpb.Event_PUT, "/a", 2),
+ event(mvccpb.Event_PUT, "/b", 2),
+ event(mvccpb.Event_PUT, "/c", 3),
+ event(mvccpb.Event_PUT, "/d", 4),
+ event(mvccpb.Event_PUT, "/e", 4),
+ event(mvccpb.Event_PUT, "/f", 5),
+ event(mvccpb.Event_PUT, "/g", 6),
+ event(mvccpb.Event_PUT, "/h", 6),
+ event(mvccpb.Event_PUT, "/i", 7),
+ event(mvccpb.Event_PUT, "/j", 7),
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ mw := newMockWatcher(16)
+ fakeClient := &clientv3.Client{
+ Watcher: mw,
+ KV: newKVStub(),
+ }
+ cache, err := New(fakeClient, "")
+ if err != nil {
+ t.Fatalf("New cache: %v", err)
+ }
+ if err != nil {
+ t.Fatalf("New cache: %v", err)
+ }
+ defer cache.Close()
+
+ mw.responses <- clientv3.WatchResponse{}
+ <-mw.registered
+
+ ctxWait, cancelWait := context.WithTimeout(t.Context(), time.Second)
+ if err := cache.WaitReady(ctxWait); err != nil {
+ t.Fatalf("cache did not become Ready(): %v", err)
+ }
+ cancelWait()
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ defer cancel()
+ watchCh := cache.Watch(ctx, "", clientv3.WithPrefix())
+
+ for _, batch := range tt.sentBatches {
+ mw.responses <- clientv3.WatchResponse{Events: batch}
+ }
+ close(mw.responses)
+
+ got := collectAndAssertAtomicEvents(ctx, t, watchCh, len(tt.wantBatch))
+
+ if diff := cmp.Diff(tt.wantBatch, got, protocmp.Transform()); diff != "" {
+ t.Fatalf("event mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestValidateWatchRange(t *testing.T) {
+ type tc struct {
+ name string
+ watchKey string
+ opts []clientv3.OpOption
+ cachePrefix string
+ wantErr bool
+ }
+
+ tests := []tc{
+ {
+ name: "single key",
+ watchKey: "/a",
+ cachePrefix: "",
+ wantErr: false,
+ },
+ {
+ name: "prefix single key",
+ watchKey: "/foo/a",
+ cachePrefix: "/foo",
+ wantErr: false,
+ },
+ {
+ name: "single key outside prefix returns error",
+ watchKey: "/z",
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "explicit range",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/b")},
+ cachePrefix: "",
+ wantErr: false,
+ },
+ {
+ name: "exact prefix range",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/b")},
+ cachePrefix: "/a",
+ wantErr: false,
+ },
+ {
+ name: "prefix subrange",
+ watchKey: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/a")},
+ cachePrefix: "/foo",
+ wantErr: false,
+ },
+ {
+ name: "reverse range returns error",
+ watchKey: "/b",
+ opts: []clientv3.OpOption{clientv3.WithRange("/a")},
+ cachePrefix: "",
+ wantErr: true,
+ },
+ {
+ name: "empty range returns error",
+ watchKey: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo")},
+ cachePrefix: "",
+ wantErr: true,
+ },
+ {
+ name: "range starting below cache prefix returns error",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo")},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "range encompassing cache prefix returns error",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/z")},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "range crossing prefixEnd returns error",
+ watchKey: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithRange("/z")},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "empty prefix",
+ watchKey: "",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "",
+ wantErr: false,
+ },
+ {
+ name: "empty prefix with cachePrefix returns error",
+ watchKey: "",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "prefix watch matches cachePrefix exactly",
+ watchKey: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "/foo",
+ wantErr: false,
+ },
+ {
+ name: "prefix watch inside cachePrefix",
+ watchKey: "/foo/bar",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "/foo",
+ wantErr: false,
+ },
+ {
+ name: "prefix starting below cachePrefix returns error",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "prefix starting above shard prefixEnd returns error",
+ watchKey: "/fop",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "fromKey open‑ended",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ cachePrefix: "",
+ wantErr: false,
+ },
+ {
+ name: "fromKey starting at prefix start",
+ watchKey: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "fromKey starting below prefixEnd",
+ watchKey: "/a",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ {
+ name: "fromKey starting above prefixEnd returns error",
+ watchKey: "/fop",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ cachePrefix: "/foo",
+ wantErr: true,
+ },
+ }
+
+ for _, c := range tests {
+ t.Run(c.name, func(t *testing.T) {
+ dummyCache := &Cache{prefix: c.cachePrefix}
+ op := clientv3.OpGet(c.watchKey, c.opts...)
+ err := dummyCache.validateRequestRange([]byte(c.watchKey), op.RangeBytes())
+ if gotErr := err != nil; gotErr != c.wantErr {
+ t.Fatalf("validateWatchRange(%q, %q, %v) err=%v, wantErr=%v",
+ c.cachePrefix, c.watchKey, c.opts, err, c.wantErr)
+ }
+ })
+ }
+}
+
+func TestCacheInvalidWatchResponseHasHeader(t *testing.T) {
+ ready := newReady()
+ ready.Set()
+ c := &Cache{
+ prefix: "/foo",
+ ready: ready,
+ }
+
+ ch := c.Watch(t.Context(), "/bar")
+
+ resp := <-ch
+ if !resp.Canceled {
+ t.Fatalf("expected canceled response, got %+v", resp)
+ }
+ if resp.Header == nil {
+ t.Fatal("expected non-nil response header")
+ }
+}
+
+func TestCacheCompactionResync(t *testing.T) {
+ firstSnapshot := &clientv3.GetResponse{
+ Header: &pb.ResponseHeader{Revision: 5},
+ Kvs: []*mvccpb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("old_value"), ModRevision: 5, CreateRevision: 5, Version: 1},
+ {Key: []byte("bar"), Value: []byte("old_bar"), ModRevision: 3, CreateRevision: 3, Version: 1},
+ },
+ }
+ secondSnapshot := &clientv3.GetResponse{
+ Header: &pb.ResponseHeader{Revision: 20},
+ Kvs: []*mvccpb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("new_value"), ModRevision: 20, CreateRevision: 5, Version: 2},
+ {Key: []byte("baz"), Value: []byte("new_baz"), ModRevision: 18, CreateRevision: 18, Version: 1},
+ },
+ }
+ fakeClient := &clientv3.Client{
+ Watcher: newMockWatcher(16),
+ KV: newKVStub(firstSnapshot, secondSnapshot),
+ }
+ cache, err := New(fakeClient, "")
+ if err != nil {
+ t.Fatalf("New cache: %v", err)
+ }
+ defer cache.Close()
+ mw := fakeClient.Watcher.(*mockWatcher)
+
+ t.Log("Phase 1: initial getWatch bootstrap")
+ mw.triggerCreatedNotify()
+ <-mw.registered
+ if err = cache.WaitReady(t.Context()); err != nil {
+ t.Fatalf("initial WaitReady: %v", err)
+ }
+ verifySnapshot(t, cache, []*mvccpb.KeyValue{
+ {Key: []byte("bar"), Value: []byte("old_bar"), ModRevision: 3, CreateRevision: 3, Version: 1},
+ {Key: []byte("foo"), Value: []byte("old_value"), ModRevision: 5, CreateRevision: 5, Version: 1},
+ })
+
+ t.Log("Phase 2: simulate compaction")
+ mw.errorCompacted(10)
+
+ waitUntil(t, time.Second, 10*time.Millisecond, func() bool { return !cache.Ready() })
+
+ ctxGet, cancelGet := context.WithTimeout(t.Context(), 100*time.Millisecond)
+ defer cancelGet()
+ snapshot, err := cache.Get(ctxGet, "foo", clientv3.WithSerializable())
+ if err != nil {
+ t.Fatalf("expected Get() to serve from cached snapshot after compaction, got %v", err)
+ }
+ if got := snapshot.Header.Revision; got != firstSnapshot.Header.Revision {
+ t.Fatalf("expected cached revision %d after compaction, got %d", firstSnapshot.Header.Revision, got)
+ }
+ if string(snapshot.Kvs[0].Value) != "old_value" {
+ t.Fatalf("expected cached value 'old_value' during compaction, got %q", string(snapshot.Kvs[0].Value))
+ }
+
+ t.Log("Phase 3: resync after compaction")
+ mw.resetRegistered()
+ mw.triggerCreatedNotify()
+ <-mw.registered
+ expectSnapshotRev := int64(20)
+ ctxResync, cancelResync := context.WithTimeout(t.Context(), time.Second)
+ defer cancelResync()
+ if err = cache.WaitForRevision(ctxResync, expectSnapshotRev); err != nil {
+ t.Fatalf("cache failed to resync to rev=%d within 1s: %v", expectSnapshotRev, err)
+ }
+
+ expectedWatchStart := secondSnapshot.Header.Revision + 1
+ if gotWatchStart := mw.getLastStartRev(); gotWatchStart != expectedWatchStart {
+ t.Errorf("Watch started at rev=%d; want %d", gotWatchStart, expectedWatchStart)
+ }
+
+ gotSnapshot, err := cache.Get(t.Context(), "foo", clientv3.WithSerializable())
+ if err != nil {
+ t.Fatalf("Get after resync: %v", err)
+ }
+ if gotSnapshot.Header.Revision != expectSnapshotRev {
+ t.Errorf("unexpected Snapshot revision: got=%d, want=%d", gotSnapshot.Header.Revision, expectSnapshotRev)
+ }
+ verifySnapshot(t, cache, []*mvccpb.KeyValue{
+ {Key: []byte("baz"), Value: []byte("new_baz"), ModRevision: 18, CreateRevision: 18, Version: 1},
+ {Key: []byte("foo"), Value: []byte("new_value"), ModRevision: 20, CreateRevision: 5, Version: 2},
+ })
+}
+
+func waitUntil(t *testing.T, timeout, poll time.Duration, cond func() bool) {
+ deadline := time.Now().Add(timeout)
+ for time.Now().Before(deadline) {
+ if cond() {
+ return
+ }
+ time.Sleep(poll)
+ }
+ t.Fatalf("condition not satisfied within %s", timeout)
+}
+
+type mockWatcher struct {
+ responses chan clientv3.WatchResponse
+ registered chan struct{}
+ closeOnce sync.Once
+ wg sync.WaitGroup
+ mu sync.Mutex
+ lastStartRev int64
+ progressErr error
+}
+
+func newMockWatcher(buf int) *mockWatcher {
+ return &mockWatcher{
+ responses: make(chan clientv3.WatchResponse, buf),
+ registered: make(chan struct{}),
+ }
+}
+
+func (m *mockWatcher) Watch(ctx context.Context, _ string, opts ...clientv3.OpOption) clientv3.WatchChan {
+ rev := m.extractRev(opts)
+ m.recordStartRev(rev)
+
+ m.signalRegistration()
+
+ out := make(chan clientv3.WatchResponse)
+ m.wg.Add(1)
+ go m.streamResponses(ctx, out)
+ return out
+}
+
+func (m *mockWatcher) RequestProgress(_ context.Context) error { return m.progressErr }
+
+func (m *mockWatcher) Close() error {
+ m.closeOnce.Do(func() { close(m.responses) })
+ m.wg.Wait()
+ return nil
+}
+
+func (m *mockWatcher) triggerCreatedNotify() { m.responses <- clientv3.WatchResponse{} }
+
+func (m *mockWatcher) errorCompacted(compRev int64) {
+ m.responses <- clientv3.WatchResponse{
+ Canceled: true,
+ CompactRevision: compRev,
+ }
+}
+
+func (m *mockWatcher) extractRev(opts []clientv3.OpOption) int64 {
+ var op clientv3.Op
+ for _, o := range opts {
+ o(&op)
+ }
+ return op.Rev()
+}
+
+func (m *mockWatcher) recordStartRev(rev int64) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.lastStartRev = rev
+}
+
+func (m *mockWatcher) getLastStartRev() int64 {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.lastStartRev
+}
+
+func (m *mockWatcher) signalRegistration() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ select {
+ case <-m.registered:
+ default:
+ close(m.registered)
+ }
+}
+
+func (m *mockWatcher) resetRegistered() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.registered = make(chan struct{})
+}
+
+func (m *mockWatcher) streamResponses(ctx context.Context, out chan<- clientv3.WatchResponse) {
+ defer func() {
+ close(out)
+ m.wg.Done()
+ }()
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case resp, ok := <-m.responses:
+ if !ok {
+ return
+ }
+ out <- resp
+ if resp.Canceled {
+ return
+ }
+ }
+ }
+}
+
+type kvStub struct {
+ queued []*clientv3.GetResponse
+ defaultResp *clientv3.GetResponse
+ defaultErr error
+}
+
+func newKVStub(resps ...*clientv3.GetResponse) *kvStub {
+ queue := append([]*clientv3.GetResponse(nil), resps...)
+ return &kvStub{
+ queued: queue,
+ defaultResp: &clientv3.GetResponse{Header: &pb.ResponseHeader{Revision: 0}},
+ }
+}
+
+func (s *kvStub) Get(_ context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
+ if s.defaultErr != nil {
+ return nil, s.defaultErr
+ }
+
+ if len(s.queued) > 0 {
+ next := s.queued[0]
+ s.queued = s.queued[1:]
+ return next, nil
+ }
+ return s.defaultResp, nil
+}
+
+func (s *kvStub) Put(ctx context.Context, key, val string, _ ...clientv3.OpOption) (*clientv3.PutResponse, error) {
+ return nil, nil
+}
+
+func (s *kvStub) Delete(ctx context.Context, key string, _ ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
+ return nil, nil
+}
+
+func (s *kvStub) Compact(ctx context.Context, rev int64, _ ...clientv3.CompactOption) (*clientv3.CompactResponse, error) {
+ return nil, nil
+}
+
+func (s *kvStub) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (clientv3.GetStreamChan, error) {
+ return nil, errors.New("GetStream not implemented")
+}
+
+func (s *kvStub) Do(ctx context.Context, op clientv3.Op) (clientv3.OpResponse, error) {
+ return clientv3.OpResponse{}, nil
+}
+
+func (s *kvStub) Txn(ctx context.Context) clientv3.Txn {
+ return nil
+}
+
+func event(eventType mvccpb.Event_EventType, key string, rev int64) *clientv3.Event {
+ return &clientv3.Event{
+ Type: eventType,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte(key),
+ ModRevision: rev,
+ CreateRevision: rev,
+ Version: 1,
+ },
+ }
+}
+
+func collectAndAssertAtomicEvents(ctx context.Context, t *testing.T, watchCh clientv3.WatchChan, wantCount int) []*clientv3.Event {
+ t.Helper()
+ var events []*clientv3.Event
+ var lastRevision int64
+ for {
+ select {
+ case <-ctx.Done():
+ t.Fatalf("timed out waiting for events (%d/%d received)",
+ len(events), wantCount)
+
+ case resp, ok := <-watchCh:
+ if !ok {
+ return events
+ }
+ if len(resp.Events) != 0 && resp.Events[0].Kv.ModRevision == lastRevision {
+ t.Fatalf("same revision found as in previous response: %d", lastRevision)
+ }
+ for _, ev := range resp.Events {
+ if ev.Kv.ModRevision < lastRevision {
+ t.Fatalf("revision went backwards: last %d, now %d", lastRevision, ev.Kv.ModRevision)
+ }
+ events = append(events, ev)
+ lastRevision = ev.Kv.ModRevision
+ }
+ if wantCount != 0 && len(events) >= wantCount {
+ return events
+ }
+ }
+ }
+}
+
+func verifySnapshot(t *testing.T, cache *Cache, want []*mvccpb.KeyValue) {
+ resp, err := cache.Get(t.Context(), "", clientv3.WithPrefix(), clientv3.WithSerializable())
+ if err != nil {
+ t.Fatalf("Get all keys: %v", err)
+ }
+
+ if diff := cmp.Diff(want, resp.Kvs, protocmp.Transform()); diff != "" {
+ t.Fatalf("cache snapshot mismatch (-want +got):\n%s", diff)
+ }
+}
+
+type noopProgressNotifier struct{}
+
+func (n *noopProgressNotifier) RequestProgress(_ context.Context) error {
+ return nil
+}
+
+func newTestProgressRequestor() *conditionalProgressRequestor {
+ return newConditionalProgressRequestor(&noopProgressNotifier{}, realClock{}, 100*time.Millisecond)
+}
+
+func newCacheForWaitTest(serverRev int64, localRev int64, pr progressRequestor) (*Cache, *store) {
+ cfg := defaultConfig()
+ st := newStore(cfg.BTreeDegree, cfg.HistoryWindowSize)
+ if localRev > 0 {
+ st.Restore(nil, localRev)
+ }
+ kv := &kvStub{
+ defaultResp: &clientv3.GetResponse{Header: &pb.ResponseHeader{Revision: serverRev}},
+ }
+ return &Cache{
+ kv: kv,
+ store: st,
+ prefix: "/",
+ progressRequestor: pr,
+ cfg: cfg,
+ }, st
+}
+
+func setupWatcherWithFakeClock(t *testing.T, opts ...clientv3.OpOption) (clientv3.WatchChan, *mockWatcher, *fakeClock) {
+ t.Helper()
+ fc := newFakeClock()
+ mw := newMockWatcher(8)
+ fakeClient := &clientv3.Client{
+ Watcher: mw,
+ KV: newKVStub(),
+ }
+ cfg := defaultConfig()
+ cfg.ProgressNotifyInterval = 100 * time.Millisecond
+ c, err := newCache(fakeClient, "", cfg, fc)
+ if err != nil {
+ t.Fatalf("newCache: %v", err)
+ }
+ t.Cleanup(c.Close)
+
+ mw.responses <- clientv3.WatchResponse{}
+ <-mw.registered
+
+ ctxWait, cancelWait := context.WithTimeout(t.Context(), time.Second)
+ defer cancelWait()
+ if err := c.WaitReady(ctxWait); err != nil {
+ t.Fatalf("cache did not become Ready: %v", err)
+ }
+
+ watchOpts := append([]clientv3.OpOption{clientv3.WithPrefix()}, opts...)
+ watchCh := c.Watch(t.Context(), "", watchOpts...)
+
+ // Seed an event so the cache has a known revision (rev 5),
+ // then drain it from the watch channel.
+ mw.responses <- clientv3.WatchResponse{
+ Events: []*clientv3.Event{event(mvccpb.Event_PUT, "/a", 5)},
+ }
+ readResponse(t, watchCh)
+
+ return watchCh, mw, fc
+}
+
+func TestCacheWatchProgressNotify(t *testing.T) {
+ t.Run("watcher requesting notification, receives them periodically", func(t *testing.T) {
+ progressCh, _, fc := setupWatcherWithFakeClock(t, clientv3.WithProgressNotify())
+
+ t.Log("First interval — progress notification arrives")
+ fc.Advance(100 * time.Millisecond)
+ resp := readResponse(t, progressCh)
+ if !resp.IsProgressNotify() {
+ t.Fatalf("expected progress notify, got events: %v", resp.Events)
+ }
+
+ t.Log("Second interval — another progress notification arrives")
+ fc.Advance(100 * time.Millisecond)
+ resp = readResponse(t, progressCh)
+ if !resp.IsProgressNotify() {
+ t.Fatalf("expected progress notify, got events: %v", resp.Events)
+ }
+ })
+
+ t.Run("watcher that didn't request progress doesn't receive any", func(t *testing.T) {
+ plainCh, _, fc := setupWatcherWithFakeClock(t)
+
+ t.Log("Advance past the interval — plain watcher should not receive anything")
+ fc.Advance(150 * time.Millisecond)
+
+ select {
+ case got, ok := <-plainCh:
+ if ok {
+ t.Fatalf("expected no response on plain watcher, got: IsProgressNotify=%v events=%v", got.IsProgressNotify(), got.Events)
+ }
+ default:
+ }
+ })
+
+ t.Run("event resets timer and delays sending progress", func(t *testing.T) {
+ progressCh, mw, fc := setupWatcherWithFakeClock(t, clientv3.WithProgressNotify())
+
+ t.Log("Advance partway into the interval, then deliver an event to reset the timer")
+ fc.Advance(50 * time.Millisecond)
+ mw.responses <- clientv3.WatchResponse{
+ Events: []*clientv3.Event{event(mvccpb.Event_PUT, "/b", 6)},
+ }
+ readResponse(t, progressCh)
+
+ t.Log("100 ms since original start but only 50 ms since event reset — no progress notify")
+ fc.Advance(50 * time.Millisecond)
+ select {
+ case got, ok := <-progressCh:
+ if ok {
+ t.Fatalf("expected no progress notify within interval after event, got: IsProgressNotify=%v events=%v", got.IsProgressNotify(), got.Events)
+ }
+ default:
+ }
+
+ t.Log("Full interval after the event — progress notify arrives")
+ fc.Advance(50 * time.Millisecond)
+ resp := readResponse(t, progressCh)
+ if !resp.IsProgressNotify() {
+ t.Fatalf("expected progress notify, got events: %v", resp.Events)
+ }
+ if resp.Header.Revision != 6 {
+ t.Fatalf("expected progress revision 6, got %d", resp.Header.Revision)
+ }
+ })
+}
+
+func TestWaitTillRevision(t *testing.T) {
+ t.Run("cache_already_caught_up", func(t *testing.T) {
+ c, _ := newCacheForWaitTest(10, 10, newTestProgressRequestor())
+
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ defer cancel()
+ if err := c.waitTillRevision(ctx, 10); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ })
+
+ t.Run("local_rev_sufficient_skips_server_call", func(t *testing.T) {
+ cfg := defaultConfig()
+ st := newStore(cfg.BTreeDegree, cfg.HistoryWindowSize)
+ st.Restore(nil, 10)
+ c := &Cache{
+ kv: &kvStub{defaultErr: fmt.Errorf("should not be called")},
+ store: st,
+ prefix: "/",
+ progressRequestor: newTestProgressRequestor(),
+ cfg: cfg,
+ }
+
+ if err := c.waitTillRevision(context.Background(), 5); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ })
+
+ t.Run("cache_catches_up", func(t *testing.T) {
+ c, st := newCacheForWaitTest(15, 5, newTestProgressRequestor())
+
+ go func() {
+ time.Sleep(200 * time.Millisecond)
+ st.Restore(nil, 10)
+ }()
+
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ defer cancel()
+ if err := c.waitTillRevision(ctx, 10); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ })
+
+ t.Run("rev_zero_cache_caught_up", func(t *testing.T) {
+ c, _ := newCacheForWaitTest(10, 10, newTestProgressRequestor())
+
+ if err := c.waitTillRevision(context.Background(), 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ })
+
+ t.Run("rev_zero_waits_for_server_rev", func(t *testing.T) {
+ c, st := newCacheForWaitTest(10, 5, newTestProgressRequestor())
+
+ go func() {
+ time.Sleep(200 * time.Millisecond)
+ st.Restore(nil, 10)
+ }()
+
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ defer cancel()
+ if err := c.waitTillRevision(ctx, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ })
+
+ t.Run("context_cancelled", func(t *testing.T) {
+ c, _ := newCacheForWaitTest(10, 5, newTestProgressRequestor())
+
+ ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
+ defer cancel()
+ err := c.waitTillRevision(ctx, 10)
+ if !errors.Is(err, context.DeadlineExceeded) {
+ t.Fatalf("got %v, want context.DeadlineExceeded", err)
+ }
+ })
+
+ t.Run("timeout", func(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ c, _ := newCacheForWaitTest(10, 5, newTestProgressRequestor())
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ err := c.waitTillRevision(ctx, 10)
+ if !errors.Is(err, ErrCacheTimeout) {
+ t.Fatalf("got %v, want ErrCacheTimeout", err)
+ }
+ })
+ })
+}
+
+func TestWaitTillRevisionTriggersProgressRequests(t *testing.T) {
+ fc := newFakeClock()
+ pr := newTestConditionalProgressRequestor(fc, 50*time.Millisecond)
+ c, st := newCacheForWaitTest(15, 5, pr)
+
+ // Start progress requestor
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ go pr.run(ctx)
+
+ // Wait for goroutine to start
+ time.Sleep(10 * time.Millisecond)
+
+ // Initially, no progress requests should be sent (no waiters)
+ fc.Advance(100 * time.Millisecond)
+ if err := pollConditionNoChange(func() bool {
+ return pr.progressRequestsSentCount.Load() == 0
+ }); err != nil {
+ t.Fatal("expected no progress requests without active waiters")
+ }
+
+ // Start waiting - this should trigger progress requests
+ errCh := make(chan error, 1)
+ go func() {
+ errCh <- c.waitTillRevision(context.Background(), 10)
+ }()
+
+ // Advance time and wait for progress requests to start
+ fc.Advance(50 * time.Millisecond)
+ time.Sleep(10 * time.Millisecond)
+
+ // Verify progress requests are being sent while waiting
+ if pr.progressRequestsSentCount.Load() == 0 {
+ t.Fatal("expected progress requests during wait")
+ }
+
+ // Complete the wait
+ st.Restore(nil, 15)
+
+ if err := <-errCh; err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ // After completion, progress requests should stop
+ finalCount := pr.progressRequestsSentCount.Load()
+ fc.Advance(100 * time.Millisecond)
+ if err := pollConditionNoChange(func() bool {
+ return pr.progressRequestsSentCount.Load() == finalCount
+ }); err != nil {
+ t.Fatalf("expected no new progress requests after completion, got %d initially, then changed", finalCount)
+ }
+}
diff --git a/cache/clock.go b/cache/clock.go
new file mode 100644
index 000000000000..62b33a668c76
--- /dev/null
+++ b/cache/clock.go
@@ -0,0 +1,54 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import "time"
+
+// Clock allows for injecting fake or real clocks into code
+// that needs to do arbitrary things based on time.
+type Clock interface {
+ NewTimer(d time.Duration) Timer
+}
+
+// Timer allows for injecting fake or real timers into code
+// that needs to do arbitrary things based on time.
+type Timer interface {
+ Chan() <-chan time.Time
+ Stop() bool
+ Reset(d time.Duration) bool
+}
+
+// realClock implements Clock using the standard time package.
+type realClock struct{}
+
+func (realClock) NewTimer(d time.Duration) Timer {
+ return &realTimer{timer: time.NewTimer(d)}
+}
+
+type realTimer struct {
+ timer *time.Timer
+}
+
+func (r *realTimer) Chan() <-chan time.Time {
+ return r.timer.C
+}
+
+func (r *realTimer) Stop() bool {
+ return r.timer.Stop()
+}
+
+func (r *realTimer) Reset(d time.Duration) bool {
+ return r.timer.Reset(d)
+}
diff --git a/cache/config.go b/cache/config.go
new file mode 100644
index 000000000000..025c570c9b0b
--- /dev/null
+++ b/cache/config.go
@@ -0,0 +1,100 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import "time"
+
+type Config struct {
+ // PerWatcherBufferSize caps each watcher’s buffered channel.
+ // Bigger values tolerate brief client slow-downs at the cost of extra memory.
+ PerWatcherBufferSize int
+ // HistoryWindowSize is the max events kept in memory for replay.
+ // It defines how far back the cache can replay events to lagging watchers
+ HistoryWindowSize int
+ // ResyncInterval controls how often the demux attempts to catch a lagging watcher up by replaying events from History.
+ ResyncInterval time.Duration
+ // InitialBackoff is the first delay to wait before retrying an upstream etcd Watch after it ends with an error.
+ InitialBackoff time.Duration
+ // MaxBackoff caps the exponential back-off between successive upstream watch retries.
+ MaxBackoff time.Duration
+ // GetTimeout is the timeout applied to the first Get() used to bootstrap the cache.
+ GetTimeout time.Duration
+ // WaitTimeout is the maximum time a consistent Get will wait for the local cache to catch up before returning ErrCacheTimeout.
+ WaitTimeout time.Duration
+ // BTreeDegree controls the degree (branching factor) of the in-memory B-tree store.
+ BTreeDegree int
+ // ProgressRequestInterval controls how often progress notifications are requested from the etcd watch stream during a consistent Get.
+ ProgressRequestInterval time.Duration
+ // ProgressNotifyInterval controls how often progress notifications are sent to local watchers registered with WithProgressNotify().
+ ProgressNotifyInterval time.Duration
+}
+
+// TODO: tune via performance/load tests.
+func defaultConfig() Config {
+ return Config{
+ PerWatcherBufferSize: 10,
+ HistoryWindowSize: 2048,
+ ResyncInterval: 50 * time.Millisecond,
+ InitialBackoff: 50 * time.Millisecond,
+ MaxBackoff: 2 * time.Second,
+ GetTimeout: 5 * time.Second,
+ WaitTimeout: 3 * time.Second,
+ BTreeDegree: 32,
+ ProgressRequestInterval: 100 * time.Millisecond,
+ ProgressNotifyInterval: 10 * time.Minute,
+ }
+}
+
+type Option func(*Config)
+
+func WithPerWatcherBufferSize(n int) Option {
+ return func(c *Config) { c.PerWatcherBufferSize = n }
+}
+
+func WithHistoryWindowSize(n int) Option {
+ return func(c *Config) { c.HistoryWindowSize = n }
+}
+
+func WithResyncInterval(d time.Duration) Option {
+ return func(c *Config) { c.ResyncInterval = d }
+}
+
+func WithInitialBackoff(d time.Duration) Option {
+ return func(c *Config) { c.InitialBackoff = d }
+}
+
+func WithMaxBackoff(d time.Duration) Option {
+ return func(c *Config) { c.MaxBackoff = d }
+}
+
+func WithGetTimeout(d time.Duration) Option {
+ return func(c *Config) { c.GetTimeout = d }
+}
+
+func WithBTreeDegree(n int) Option {
+ return func(c *Config) { c.BTreeDegree = n }
+}
+
+func WithProgressRequestInterval(d time.Duration) Option {
+ return func(c *Config) { c.ProgressRequestInterval = d }
+}
+
+func WithProgressNotifyInterval(d time.Duration) Option {
+ return func(c *Config) { c.ProgressNotifyInterval = d }
+}
+
+func WithWaitTimeout(d time.Duration) Option {
+ return func(c *Config) { c.WaitTimeout = d }
+}
diff --git a/cache/demux.go b/cache/demux.go
new file mode 100644
index 000000000000..9799c323d7fe
--- /dev/null
+++ b/cache/demux.go
@@ -0,0 +1,390 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "errors"
+ "sync"
+ "time"
+
+ "go.etcd.io/etcd/api/v3/etcdserverpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+type demux struct {
+ mu sync.RWMutex
+ // activeWatchers & laggingWatchers hold the first revision the watcher still needs (nextRev).
+ activeWatchers map[*watcher]int64
+ laggingWatchers map[*watcher]int64
+ resyncInterval time.Duration
+ // Range of revisions maintained for demux operations, inclusive. Broader than history as event revision is not contious.
+ // maxRev tracks highest seen revision; minRev sets watcher compaction threshold (updated to evictedRev+1 on history overflow)
+ minRev, maxRev int64
+ // History stores events within [minRev, maxRev].
+ history ringBuffer[[]*clientv3.Event]
+ // resynced is used to notify that resync loop was completed.
+ resynced *notifier
+}
+
+func NewDemux(ctx context.Context, wg *sync.WaitGroup, historyWindowSize int, resyncInterval time.Duration) *demux {
+ d := newDemux(historyWindowSize, resyncInterval)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ d.resyncLoop(ctx)
+ }()
+ return d
+}
+
+func newDemux(historyWindowSize int, resyncInterval time.Duration) *demux {
+ return &demux{
+ activeWatchers: make(map[*watcher]int64),
+ laggingWatchers: make(map[*watcher]int64),
+ history: *newRingBuffer(historyWindowSize, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision }),
+ resyncInterval: resyncInterval,
+ resynced: newNotifier(),
+ }
+}
+
+// resyncLoop periodically tries to catch lagging watchers up by replaying events from History.
+func (d *demux) resyncLoop(ctx context.Context) {
+ timer := time.NewTimer(d.resyncInterval)
+ defer timer.Stop()
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-timer.C:
+ d.resyncLaggingWatchers()
+ d.resynced.notify()
+ timer.Reset(d.resyncInterval)
+ }
+ }
+}
+
+// WaitForNextResync blocks until the next resync loop iteration is complete.
+func (d *demux) WaitForNextResync(ctx context.Context) error {
+ return d.resynced.wait(ctx)
+}
+
+func (d *demux) Register(w *watcher, startingRev int64) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ if d.maxRev == 0 {
+ if startingRev == 0 {
+ d.activeWatchers[w] = 0
+ } else {
+ d.laggingWatchers[w] = startingRev
+ }
+ return
+ }
+
+ // Special case: 0 means “newest”.
+ if startingRev == 0 {
+ startingRev = d.maxRev + 1
+ }
+
+ if startingRev <= d.maxRev {
+ d.laggingWatchers[w] = startingRev
+ } else {
+ d.activeWatchers[w] = startingRev
+ }
+}
+
+func (d *demux) Unregister(w *watcher) {
+ func() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ delete(d.activeWatchers, w)
+ delete(d.laggingWatchers, w)
+ }()
+ w.Stop()
+}
+
+func (d *demux) Init(minRev int64) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ if minRev == 0 {
+ return
+ }
+ if d.minRev == 0 {
+ // Watch started for empty demux
+ d.minRev = minRev
+ return
+ }
+ if d.maxRev == 0 {
+ // Watch started on initialized demux that never got any event.
+ d.purge()
+ d.minRev = minRev
+ return
+ }
+ if minRev == d.maxRev+1 {
+ // Watch continuing from last revision it observed.
+ return
+ }
+ // Watch opened on revision mismatching dmux last observed revision.
+ d.purge()
+ d.minRev = minRev
+}
+
+func (d *demux) Broadcast(resp clientv3.WatchResponse) error {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ if d.minRev == 0 {
+ return errors.New("demux: not initialized")
+ }
+ err := validateRevisions(resp, d.maxRev)
+ if err != nil {
+ return err
+ }
+ d.updateStoreLocked(resp)
+ d.broadcastLocked(resp)
+ return nil
+}
+
+func (d *demux) LatestRev() int64 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ return d.maxRev
+}
+
+func (d *demux) BroadcastProgress() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ if d.maxRev == 0 {
+ return
+ }
+ resp := clientv3.WatchResponse{
+ Header: &etcdserverpb.ResponseHeader{
+ Revision: d.maxRev,
+ },
+ }
+ for w := range d.activeWatchers {
+ w.enqueueResponse(resp)
+ }
+}
+
+func (d *demux) BroadcastProgressTo(w *watcher) {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ if d.maxRev == 0 {
+ return
+ }
+ if _, active := d.activeWatchers[w]; !active {
+ return
+ }
+ resp := clientv3.WatchResponse{
+ Header: &etcdserverpb.ResponseHeader{
+ Revision: d.maxRev,
+ },
+ }
+ w.enqueueResponse(resp)
+}
+
+func (d *demux) updateStoreLocked(resp clientv3.WatchResponse) {
+ if resp.IsProgressNotify() {
+ d.maxRev = resp.Header.Revision
+ return
+ }
+ if len(resp.Events) == 0 {
+ return
+ }
+ events := resp.Events
+ batchStart := 0
+ for end := 1; end < len(events); end++ {
+ if events[end].Kv.ModRevision != events[batchStart].Kv.ModRevision {
+ if end > batchStart {
+ if end+1 == len(events) && d.history.full() {
+ d.minRev = d.history.PeekOldest() + 1
+ }
+ d.history.Append(events[batchStart:end])
+ }
+ batchStart = end
+ }
+ }
+ if batchStart < len(events) {
+ if d.history.full() {
+ d.minRev = d.history.PeekOldest() + 1
+ }
+ d.history.Append(events[batchStart:])
+ }
+ d.maxRev = events[len(events)-1].Kv.ModRevision
+}
+
+func (d *demux) broadcastLocked(resp clientv3.WatchResponse) {
+ switch {
+ case resp.IsProgressNotify():
+ d.broadcastProgressLocked(resp.Header.Revision)
+ case len(resp.Events) != 0:
+ d.broadcastEventsLocked(resp.Events)
+ default:
+ }
+}
+
+func (d *demux) broadcastProgressLocked(progressRev int64) {
+ for w, nextRev := range d.activeWatchers {
+ if nextRev >= progressRev {
+ continue
+ }
+ resp := clientv3.WatchResponse{
+ Header: &etcdserverpb.ResponseHeader{
+ Revision: progressRev,
+ },
+ }
+ if w.enqueueResponse(resp) {
+ d.activeWatchers[w] = progressRev + 1
+ }
+ }
+}
+
+func (d *demux) broadcastEventsLocked(events []*clientv3.Event) {
+ firstRev := events[0].Kv.ModRevision
+ lastRev := events[len(events)-1].Kv.ModRevision
+
+ for w, nextRev := range d.activeWatchers {
+ if nextRev != 0 && firstRev > nextRev {
+ d.laggingWatchers[w] = nextRev
+ delete(d.activeWatchers, w)
+ continue
+ }
+
+ sendStart := len(events)
+ for i, ev := range events {
+ if ev.Kv.ModRevision >= nextRev {
+ sendStart = i
+ break
+ }
+ }
+
+ if sendStart == len(events) {
+ continue
+ }
+
+ if !w.enqueueResponse(clientv3.WatchResponse{
+ Events: events[sendStart:],
+ }) { // overflow → lagging
+ d.laggingWatchers[w] = nextRev
+ delete(d.activeWatchers, w)
+ } else {
+ d.activeWatchers[w] = lastRev + 1
+ }
+ }
+}
+
+// Purge stops all watchers and rebase history on watch errors
+func (d *demux) Purge() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.purge()
+}
+
+func (d *demux) purge() {
+ d.maxRev = 0
+ d.minRev = 0
+ d.history.RebaseHistory()
+ for w := range d.activeWatchers {
+ w.Stop()
+ }
+ for w := range d.laggingWatchers {
+ w.Stop()
+ }
+ d.activeWatchers = make(map[*watcher]int64)
+ d.laggingWatchers = make(map[*watcher]int64)
+}
+
+// Compact is called when etcd reports a compaction at compactRev to rebase history;
+// it keeps provably-too-old watchers for later cancellation, stops others, and clients should resubscribe.
+func (d *demux) Compact(compactRev int64) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.purge()
+}
+
+func (d *demux) resyncLaggingWatchers() {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ if d.minRev == 0 {
+ return
+ }
+
+ for w, nextRev := range d.laggingWatchers {
+ if nextRev < d.minRev {
+ w.Compact(nextRev)
+ delete(d.laggingWatchers, w)
+ continue
+ }
+ // TODO: re-enable key‐predicate in Filter when non‐zero startRev or performance tuning is needed
+ resyncSuccess := true
+ d.history.AscendGreaterOrEqual(nextRev, func(rev int64, eventBatch []*clientv3.Event) bool {
+ resp := clientv3.WatchResponse{
+ Events: eventBatch,
+ }
+ if !w.enqueueResponse(resp) { // buffer overflow: watcher still lagging
+ resyncSuccess = false
+ return false
+ }
+ nextRev = rev + 1
+ return true
+ })
+ // Send progress to just resync.
+ if resyncSuccess {
+ resp := clientv3.WatchResponse{
+ Header: &etcdserverpb.ResponseHeader{Revision: d.maxRev},
+ }
+ if d.maxRev > nextRev && w.enqueueResponse(resp) {
+ nextRev = d.maxRev + 1
+ }
+ delete(d.laggingWatchers, w)
+ d.activeWatchers[w] = nextRev
+ } else {
+ d.laggingWatchers[w] = nextRev
+ }
+ }
+}
+
+func newNotifier() *notifier {
+ return ¬ifier{
+ ch: make(chan struct{}),
+ }
+}
+
+type notifier struct {
+ mu sync.RWMutex
+ ch chan struct{}
+}
+
+func (n *notifier) notify() {
+ n.mu.Lock()
+ defer n.mu.Unlock()
+ previous := n.ch
+ n.ch = make(chan struct{})
+ close(previous)
+}
+
+func (n *notifier) wait(ctx context.Context) error {
+ n.mu.RLock()
+ ch := n.ch
+ n.mu.RUnlock()
+ select {
+ case <-ch:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
diff --git a/cache/demux_test.go b/cache/demux_test.go
new file mode 100644
index 000000000000..950d7a0bbd3b
--- /dev/null
+++ b/cache/demux_test.go
@@ -0,0 +1,517 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/stretchr/testify/require"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+func TestInit(t *testing.T) {
+ type want struct {
+ min int64
+ max int64
+ historyRevs []int64
+ }
+ tests := []struct {
+ name string
+ capacity int
+ initRev int64
+ eventRevs []int64
+ shouldReinit bool
+ reinitRev int64
+ want want
+ }{
+ {
+ name: "first init sets only min",
+ capacity: 8,
+ initRev: 5,
+ eventRevs: nil,
+ shouldReinit: false,
+ want: want{min: 5, max: 0, historyRevs: nil},
+ },
+ {
+ name: "init on empty demux with events",
+ capacity: 8,
+ initRev: 5,
+ eventRevs: []int64{7, 9, 13},
+ shouldReinit: false,
+ want: want{min: 5, max: 13, historyRevs: []int64{7, 9, 13}},
+ },
+ {
+ name: "continuation at max+1 preserves range and history",
+ capacity: 8,
+ initRev: 10,
+ eventRevs: []int64{13, 15, 21},
+ shouldReinit: true,
+ reinitRev: 22,
+ want: want{min: 10, max: 21, historyRevs: []int64{13, 15, 21}},
+ },
+ {
+ name: "gap from max triggers purge and clears history",
+ capacity: 8,
+ initRev: 10,
+ eventRevs: []int64{13, 15, 21},
+ shouldReinit: true,
+ reinitRev: 30,
+ want: want{min: 30, max: 0, historyRevs: nil},
+ },
+ {
+ name: "idempotent reinit at same revision clears history",
+ capacity: 8,
+ initRev: 7,
+ eventRevs: []int64{8, 9, 10},
+ shouldReinit: true,
+ reinitRev: 7,
+ want: want{min: 7, max: 0, historyRevs: nil},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ d := newDemux(tt.capacity, 10*time.Millisecond)
+
+ d.Init(tt.initRev)
+
+ if len(tt.eventRevs) > 0 {
+ if err := d.Broadcast(respWithEventRevs(tt.eventRevs...)); err != nil {
+ t.Fatalf("Broadcast(%v) failed: %v", tt.eventRevs, err)
+ }
+ }
+
+ if tt.shouldReinit {
+ d.Init(tt.reinitRev)
+ }
+
+ if d.minRev != tt.want.min || d.maxRev != tt.want.max {
+ t.Fatalf("revision range: got(min=%d, max=%d), want(min=%d, max=%d)",
+ d.minRev, d.maxRev, tt.want.min, tt.want.max)
+ }
+
+ var actualHistoryRevs []int64
+ d.history.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
+ actualHistoryRevs = append(actualHistoryRevs, rev)
+ return true
+ })
+
+ if diff := cmp.Diff(tt.want.historyRevs, actualHistoryRevs); diff != "" {
+ t.Fatalf("history validation failed (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestBroadcast(t *testing.T) {
+ type want struct {
+ min int64
+ max int64
+ shouldError bool
+ }
+
+ tests := []struct {
+ name string
+ capacity int
+ initRev int64
+ initialRevs []int64
+ followupRevs []int64
+ want want
+ }{
+ {
+ name: "history not full",
+ capacity: 2,
+ initRev: 1,
+ initialRevs: []int64{2},
+ want: want{min: 1, max: 2, shouldError: false},
+ },
+ {
+ name: "history at exact capacity",
+ capacity: 2,
+ initRev: 1,
+ initialRevs: []int64{2, 3},
+ want: want{min: 1, max: 3, shouldError: false},
+ },
+ {
+ name: "history overflow with eviction",
+ capacity: 2,
+ initRev: 1,
+ initialRevs: []int64{2, 3, 4},
+ want: want{min: 3, max: 4, shouldError: false},
+ },
+ {
+ name: "history overflow not continuous",
+ capacity: 2,
+ initRev: 2,
+ initialRevs: []int64{4, 8, 16},
+ want: want{min: 5, max: 16, shouldError: false},
+ },
+ {
+ name: "empty broadcast is no-op",
+ capacity: 8,
+ initRev: 10,
+ initialRevs: []int64{},
+ want: want{min: 10, max: 0, shouldError: false},
+ },
+ {
+ name: "revisions below maxRev are rejected",
+ capacity: 8,
+ initRev: 4,
+ initialRevs: []int64{5, 6},
+ followupRevs: []int64{4},
+ want: want{shouldError: true},
+ },
+ {
+ name: "revisions equal to maxRev are rejected",
+ capacity: 8,
+ initRev: 4,
+ initialRevs: []int64{5, 6},
+ followupRevs: []int64{6},
+ want: want{shouldError: true},
+ },
+ {
+ name: "revisions above maxRev are accepted",
+ capacity: 8,
+ initRev: 4,
+ initialRevs: []int64{5, 6},
+ followupRevs: []int64{9, 14, 17},
+ want: want{min: 4, max: 17, shouldError: false},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ d := newDemux(tt.capacity, 10*time.Millisecond)
+ d.Init(tt.initRev)
+
+ if len(tt.initialRevs) > 0 {
+ if err := d.Broadcast(respWithEventRevs(tt.initialRevs...)); err != nil {
+ t.Fatalf("unexpected error broadcasting initial revisions %v: %v", tt.initialRevs, err)
+ }
+ }
+
+ if len(tt.followupRevs) > 0 {
+ err := d.Broadcast(respWithEventRevs(tt.followupRevs...))
+ if tt.want.shouldError {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ }
+
+ if d.minRev != tt.want.min || d.maxRev != tt.want.max {
+ t.Fatalf("revision range: got(min=%d, max=%d), want(min=%d, max=%d)",
+ d.minRev, d.maxRev, tt.want.min, tt.want.max)
+ }
+ })
+ }
+}
+
+func TestBroadcastBatching(t *testing.T) {
+ tests := []struct {
+ name string
+ input []int64
+ wantRevs []int64
+ wantSizes []int
+ }{
+ {
+ name: "two groups",
+ input: []int64{14, 14, 15, 15, 15},
+ wantRevs: []int64{14},
+ wantSizes: []int{5},
+ },
+ {
+ name: "single group",
+ input: []int64{7, 7, 7},
+ wantRevs: []int64{7},
+ wantSizes: []int{3},
+ },
+ {
+ name: "all distinct",
+ input: []int64{1, 2, 3},
+ wantRevs: []int64{1},
+ wantSizes: []int{3},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ w := newWatcher(len(tt.input)+1, nil)
+ d.Init(1)
+ d.Register(w, 0)
+
+ d.Broadcast(respWithEventRevs(tt.input...))
+
+ gotRevs, gotSizes := readBatches(t, w, len(tt.wantRevs))
+
+ if diff := cmp.Diff(tt.wantRevs, gotRevs); diff != "" {
+ t.Fatalf("revision mismatch (-want +got):\n%s", diff)
+ }
+ if diff := cmp.Diff(tt.wantSizes, gotSizes); diff != "" {
+ t.Fatalf("batch size mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestSlowWatcherResync(t *testing.T) {
+ tests := []struct {
+ name string
+ input []int64
+ wantInitialRevs []int64
+ wantInitialSizes []int
+ wantResyncRevs []int64
+ wantResyncSizes []int
+ }{
+ {
+ name: "single event overflow",
+ input: []int64{1, 2, 3},
+ wantInitialRevs: []int64{1},
+ wantInitialSizes: []int{3},
+ wantResyncRevs: []int64{},
+ wantResyncSizes: []int{},
+ },
+ {
+ name: "multi events batch overflow",
+ input: []int64{10, 10, 11, 12, 12},
+ wantInitialRevs: []int64{10},
+ wantInitialSizes: []int{5},
+ wantResyncRevs: []int64{},
+ wantResyncSizes: []int{},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ w := newWatcher(1, nil)
+ d.Init(1)
+ d.Register(w, 0)
+
+ d.Broadcast(respWithEventRevs(tt.input...))
+
+ gotInitRevs, gotInitSizes := readBatches(t, w, len(tt.wantInitialRevs))
+ if diff := cmp.Diff(tt.wantInitialRevs, gotInitRevs); diff != "" {
+ t.Fatalf("initial revs mismatch (-want +got):\n%s", diff)
+ }
+ if diff := cmp.Diff(tt.wantInitialSizes, gotInitSizes); diff != "" {
+ t.Fatalf("initial batch sizes mismatch (-want +got):\n%s", diff)
+ }
+
+ gotRevs, gotSizes := make([]int64, 0, len(tt.wantResyncRevs)), make([]int, 0, len(tt.wantResyncRevs))
+ for len(gotRevs) < len(tt.wantResyncRevs) {
+ d.resyncLaggingWatchers()
+ revs, sizes := readBatches(t, w, 1)
+ gotRevs = append(gotRevs, revs...)
+ gotSizes = append(gotSizes, sizes...)
+ }
+ if diff := cmp.Diff(tt.wantResyncRevs, gotRevs); diff != "" {
+ t.Fatalf("resync revs mismatch (-want +got):\n%s", diff)
+ }
+ if diff := cmp.Diff(tt.wantResyncSizes, gotSizes); diff != "" {
+ t.Fatalf("resync batch sizes mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestWatcherCompactResponseHasHeader(t *testing.T) {
+ w := newWatcher(1, nil)
+
+ w.Compact(7)
+
+ require.NotNil(t, w.cancelResp.Header)
+ require.True(t, w.cancelResp.Canceled)
+ require.Equal(t, int64(7), w.cancelResp.CompactRevision)
+}
+
+func TestBroadcastProgress(t *testing.T) {
+ t.Run("sends progress to active watchers", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+ d.maxRev = 10
+
+ w1 := newWatcher(8, nil)
+ w2 := newWatcher(8, nil)
+ d.Register(w1, 0)
+ d.Register(w2, 0)
+
+ d.BroadcastProgress()
+
+ resp1 := readResponse(t, w1.respCh)
+ require.Truef(t, resp1.IsProgressNotify(), "expected progress notify")
+ require.Equal(t, int64(10), resp1.Header.Revision)
+
+ resp2 := readResponse(t, w2.respCh)
+ require.Truef(t, resp2.IsProgressNotify(), "expected progress notify")
+ require.Equal(t, int64(10), resp2.Header.Revision)
+ })
+
+ t.Run("is no-op when maxRev is zero", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+
+ w := newWatcher(8, nil)
+ d.Register(w, 0)
+
+ d.maxRev = 0
+ d.BroadcastProgress()
+ select {
+ case <-w.respCh:
+ t.Fatal("expected no response when maxRev is 0")
+ default:
+ }
+ })
+
+ t.Run("sends progress only to active watchers", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+ d.maxRev = 10
+
+ active := newWatcher(8, nil)
+ lagging := newWatcher(8, nil)
+ d.Register(active, 0) // startingRev becomes maxRev+1 => active
+ d.Register(lagging, 5)
+
+ d.BroadcastProgress()
+
+ activeResp := readResponse(t, active.respCh)
+ require.Truef(t, activeResp.IsProgressNotify(), "expected progress notify for active watcher")
+ require.Equal(t, int64(10), activeResp.Header.Revision)
+
+ select {
+ case <-lagging.respCh:
+ t.Fatal("expected no progress notify for lagging watcher")
+ default:
+ }
+ })
+}
+
+func TestBroadcastProgressTo(t *testing.T) {
+ t.Run("sends progress only to the target watcher", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+ d.maxRev = 10
+
+ target := newWatcher(8, nil)
+ other := newWatcher(8, nil)
+ d.Register(target, 0)
+ d.Register(other, 0)
+
+ d.BroadcastProgressTo(target)
+
+ resp := readResponse(t, target.respCh)
+ require.Truef(t, resp.IsProgressNotify(), "expected progress notify")
+ require.Equal(t, int64(10), resp.Header.Revision)
+
+ select {
+ case <-other.respCh:
+ t.Fatal("expected no progress notify for other watcher")
+ default:
+ }
+ })
+
+ t.Run("is no-op when maxRev is zero", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+
+ w := newWatcher(8, nil)
+ d.Register(w, 0)
+
+ d.maxRev = 0
+ d.BroadcastProgressTo(w)
+
+ select {
+ case <-w.respCh:
+ t.Fatal("expected no response when maxRev is 0")
+ default:
+ }
+ })
+
+ t.Run("is no-op for lagging watcher", func(t *testing.T) {
+ d := newDemux(16, 10*time.Millisecond)
+ d.Init(1)
+ d.maxRev = 10
+
+ w := newWatcher(8, nil)
+ d.Register(w, 5) // startingRev <= maxRev => lagging
+
+ d.BroadcastProgressTo(w)
+
+ select {
+ case <-w.respCh:
+ t.Fatal("expected no progress notify for lagging watcher")
+ default:
+ }
+ })
+}
+
+func readResponse(t *testing.T, ch <-chan clientv3.WatchResponse) clientv3.WatchResponse {
+ t.Helper()
+ select {
+ case resp := <-ch:
+ return resp
+ case <-time.After(2 * time.Second):
+ t.Fatalf("timed out waiting for response")
+ return clientv3.WatchResponse{}
+ }
+}
+
+func respWithEventRevs(revs ...int64) clientv3.WatchResponse {
+ events := make([]*clientv3.Event, 0, len(revs))
+ for _, r := range revs {
+ kv := &mvccpb.KeyValue{
+ Key: []byte("k"),
+ Value: []byte("v"),
+ ModRevision: r,
+ }
+ events = append(events, &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: kv,
+ })
+ }
+ return clientv3.WatchResponse{Events: events}
+}
+
+func readBatches(t *testing.T, w *watcher, n int) (revs []int64, sizes []int) {
+ t.Helper()
+ timeout := time.After(2 * time.Second)
+ for len(revs) < n {
+ select {
+ case resp := <-w.respCh:
+ if resp.Canceled {
+ t.Fatalf("unexpected canceled response in test: %v", resp.CancelReason)
+ }
+ if len(resp.Events) == 0 {
+ continue
+ }
+ require.NotNil(t, resp.Header)
+ revs = append(revs, resp.Events[0].Kv.ModRevision)
+ sizes = append(sizes, len(resp.Events))
+ case <-timeout:
+ t.Fatalf("timed out waiting for %d batches; got %d", n, len(revs))
+ }
+ }
+ return revs, sizes
+}
diff --git a/cache/fake_clock_test.go b/cache/fake_clock_test.go
new file mode 100644
index 000000000000..65bec2f8fa67
--- /dev/null
+++ b/cache/fake_clock_test.go
@@ -0,0 +1,111 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+// Minimal fake clock for testing, based on
+// https://github.com/kubernetes/utils/blob/master/clock/testing/fake_clock.go
+
+import (
+ "sync"
+ "time"
+)
+
+type fakeClock struct {
+ lock sync.Mutex
+ time time.Time
+ waiters []*fakeClockWaiter
+}
+
+type fakeClockWaiter struct {
+ targetTime time.Time
+ destChan chan time.Time
+}
+
+func newFakeClock() *fakeClock {
+ return &fakeClock{time: time.Now()}
+}
+
+func (f *fakeClock) NewTimer(d time.Duration) Timer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ ch := make(chan time.Time, 1)
+ timer := &fakeTimer{
+ fakeClock: f,
+ waiter: fakeClockWaiter{
+ targetTime: f.time.Add(d),
+ destChan: ch,
+ },
+ }
+ f.waiters = append(f.waiters, &timer.waiter)
+ return timer
+}
+
+func (f *fakeClock) Advance(d time.Duration) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ f.time = f.time.Add(d)
+ newWaiters := make([]*fakeClockWaiter, 0, len(f.waiters))
+ for _, w := range f.waiters {
+ if !w.targetTime.After(f.time) {
+ w.destChan <- f.time
+ } else {
+ newWaiters = append(newWaiters, w)
+ }
+ }
+ f.waiters = newWaiters
+}
+
+type fakeTimer struct {
+ fakeClock *fakeClock
+ waiter fakeClockWaiter
+}
+
+func (t *fakeTimer) Chan() <-chan time.Time {
+ return t.waiter.destChan
+}
+
+func (t *fakeTimer) Stop() bool {
+ t.fakeClock.lock.Lock()
+ defer t.fakeClock.lock.Unlock()
+ active := false
+ newWaiters := make([]*fakeClockWaiter, 0, len(t.fakeClock.waiters))
+ for _, w := range t.fakeClock.waiters {
+ if w != &t.waiter {
+ newWaiters = append(newWaiters, w)
+ } else {
+ active = true
+ }
+ }
+ t.fakeClock.waiters = newWaiters
+ return active
+}
+
+func (t *fakeTimer) Reset(d time.Duration) bool {
+ t.fakeClock.lock.Lock()
+ defer t.fakeClock.lock.Unlock()
+ active := false
+ t.waiter.targetTime = t.fakeClock.time.Add(d)
+ for _, w := range t.fakeClock.waiters {
+ if w == &t.waiter {
+ // If timer is found, it has not been fired yet.
+ active = true
+ break
+ }
+ }
+ if !active {
+ t.fakeClock.waiters = append(t.fakeClock.waiters, &t.waiter)
+ }
+ return active
+}
diff --git a/cache/go.mod b/cache/go.mod
new file mode 100644
index 000000000000..00d2ca1e23e4
--- /dev/null
+++ b/cache/go.mod
@@ -0,0 +1,39 @@
+module go.etcd.io/etcd/cache/v3
+
+go 1.26
+
+toolchain go1.26.5
+
+require (
+ github.com/google/go-cmp v0.7.0
+ github.com/stretchr/testify v1.11.1
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ google.golang.org/protobuf v1.36.11
+ k8s.io/utils v0.0.0-20260108192941-914a6e750570
+)
+
+require (
+ github.com/coreos/go-semver v0.3.1 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.1 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/grpc v1.81.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+replace (
+ go.etcd.io/etcd/api/v3 => ../api
+ go.etcd.io/etcd/client/pkg/v3 => ../client/pkg
+ go.etcd.io/etcd/client/v3 => ../client/v3
+)
diff --git a/cache/go.sum b/cache/go.sum
new file mode 100644
index 000000000000..89feacd77466
--- /dev/null
+++ b/cache/go.sum
@@ -0,0 +1,89 @@
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
+github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
diff --git a/cache/predicate.go b/cache/predicate.go
new file mode 100644
index 000000000000..233c4e030940
--- /dev/null
+++ b/cache/predicate.go
@@ -0,0 +1,52 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import "bytes"
+
+type Prefix string
+
+func (prefix Prefix) Match(key []byte) bool {
+ if prefix == "" {
+ return true
+ }
+ prefixLen := len(prefix)
+ return len(key) >= prefixLen && string(key[:prefixLen]) == string(prefix)
+}
+
+func ExactKey(key []byte) KeyPredicate {
+ return func(k []byte) bool { return bytes.Equal(k, key) }
+}
+
+func FromKey(start []byte) KeyPredicate {
+ return func(k []byte) bool { return bytes.Compare(k, start) >= 0 }
+}
+
+func Range(start, end []byte) KeyPredicate {
+ return func(k []byte) bool {
+ return bytes.Compare(k, start) >= 0 &&
+ bytes.Compare(k, end) < 0
+ }
+}
+
+func KeyPredForRange(start, end []byte) KeyPredicate {
+ if len(end) == 0 {
+ return ExactKey(start)
+ }
+ if len(end) == 1 && end[0] == 0 {
+ return FromKey(start)
+ }
+ return Range(start, end)
+}
diff --git a/cache/progress_requestor.go b/cache/progress_requestor.go
new file mode 100644
index 000000000000..4bde695799fa
--- /dev/null
+++ b/cache/progress_requestor.go
@@ -0,0 +1,114 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+)
+
+type progressNotifier interface {
+ RequestProgress(ctx context.Context) error
+}
+
+// Copied from https://github.com/kubernetes/kubernetes/blob/4116c15/staging/src/k8s.io/apiserver/pkg/storage/cacher/progress/watch_progress.go
+type progressRequestor interface {
+ // run starts the background loop that sends RequestProgress RPCs.
+ run(ctx context.Context)
+ // add increments the count of active waiters so the run loop knows to send RequestProgress RPCs.
+ add()
+ // remove decrements the count of active waiters.
+ remove()
+}
+
+type conditionalProgressRequestor struct {
+ mux sync.Mutex
+ cond *sync.Cond
+ waiting int32
+ stopped bool
+ notifier progressNotifier
+ requestInterval time.Duration
+ clock Clock
+}
+
+func newConditionalProgressRequestor(notifier progressNotifier, clock Clock, requestInterval time.Duration) *conditionalProgressRequestor {
+ waiter := &conditionalProgressRequestor{
+ notifier: notifier,
+ requestInterval: requestInterval,
+ clock: clock,
+ }
+ waiter.cond = sync.NewCond(&waiter.mux)
+ return waiter
+}
+
+func (p *conditionalProgressRequestor) run(ctx context.Context) {
+ go func() {
+ <-ctx.Done()
+ p.mux.Lock()
+ defer p.mux.Unlock()
+ p.stopped = true
+ p.cond.Signal()
+ }()
+
+ timer := p.clock.NewTimer(p.requestInterval)
+ defer timer.Stop()
+ for {
+ stopped := func() bool {
+ p.mux.Lock()
+ defer p.mux.Unlock()
+ for p.waiting == 0 && !p.stopped {
+ p.cond.Wait()
+ }
+ return p.stopped
+ }()
+ if stopped {
+ return
+ }
+
+ select {
+ case <-timer.Chan():
+ shouldContinue := func() bool {
+ p.mux.Lock()
+ defer p.mux.Unlock()
+ return p.waiting > 0 && !p.stopped
+ }()
+ if !shouldContinue {
+ timer.Reset(0)
+ continue
+ }
+ timer.Reset(p.requestInterval)
+ if err := p.notifier.RequestProgress(ctx); err != nil {
+ fmt.Printf("RequestProgress failed: %v\n", err)
+ }
+ case <-ctx.Done():
+ return
+ }
+ }
+}
+
+func (p *conditionalProgressRequestor) add() {
+ p.mux.Lock()
+ defer p.mux.Unlock()
+ p.waiting++
+ p.cond.Signal()
+}
+
+func (p *conditionalProgressRequestor) remove() {
+ p.mux.Lock()
+ defer p.mux.Unlock()
+ p.waiting--
+}
diff --git a/cache/progress_requestor_test.go b/cache/progress_requestor_test.go
new file mode 100644
index 000000000000..92837c6221dd
--- /dev/null
+++ b/cache/progress_requestor_test.go
@@ -0,0 +1,155 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+const (
+ pollPeriod = 1 * time.Millisecond
+ minimalNoChange = 20 * time.Millisecond
+ pollTimeout = 5 * time.Second
+)
+
+// Copied from https://github.com/kubernetes/kubernetes/blob/4116c15/staging/src/k8s.io/apiserver/pkg/storage/cacher/progress/watch_progress_test.go
+func TestConditionalProgressRequestor(t *testing.T) {
+ ctx := context.Background()
+
+ fc := newFakeClock()
+ pr := newTestConditionalProgressRequestor(fc, 100*time.Millisecond)
+ stopCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ go pr.run(stopCtx)
+
+ var wantRequestsSent int32
+ var requestsSent int32
+
+ t.Log("Wait for goroutine to start")
+ time.Sleep(10 * time.Millisecond)
+
+ t.Log("No progress requests if no-one is waiting")
+ fc.Advance(250 * time.Millisecond)
+
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+
+ t.Log("Adding waiters allows progress request to be sent")
+ pr.add()
+ fc.Advance(150 * time.Millisecond)
+ wantRequestsSent++
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+
+ t.Log("Periodically request progress to be sent every period")
+ for wantRequestsSent < 5 {
+ fc.Advance(100 * time.Millisecond)
+ wantRequestsSent++
+
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+ }
+ pr.remove()
+
+ t.Log("No progress requests if no-one is waiting")
+ fc.Advance(250 * time.Millisecond)
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+
+ t.Log("No progress after stopping")
+ cancel()
+ fc.Advance(50 * time.Millisecond)
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+ pr.add()
+ fc.Advance(250 * time.Millisecond)
+ if err := pollConditionNoChange(func() bool {
+ requestsSent = pr.progressRequestsSentCount.Load()
+ return requestsSent == wantRequestsSent
+ }); err != nil {
+ t.Fatalf("Failed to wait progress requests, err: %s, want: %d, got %d", err, wantRequestsSent, requestsSent)
+ }
+}
+
+func newTestConditionalProgressRequestor(clock Clock, requestInterval time.Duration) *testConditionalProgressRequestor {
+ pr := &testConditionalProgressRequestor{}
+ pr.conditionalProgressRequestor = newConditionalProgressRequestor(pr, clock, requestInterval)
+ return pr
+}
+
+type testConditionalProgressRequestor struct {
+ *conditionalProgressRequestor
+ progressRequestsSentCount atomic.Int32
+}
+
+func (pr *testConditionalProgressRequestor) RequestProgress(ctx context.Context) error {
+ pr.progressRequestsSentCount.Add(1)
+ return nil
+}
+
+func pollConditionNoChange(condition func() bool) error {
+ passCounter := 0
+ requiredNumberOfPasses := int(minimalNoChange/pollPeriod) + 1
+ deadline := time.Now().Add(pollTimeout)
+
+ ticker := time.NewTicker(pollPeriod)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ if condition() {
+ passCounter++
+ } else {
+ passCounter = 0
+ }
+ if passCounter >= requiredNumberOfPasses {
+ return nil
+ }
+ if time.Now().After(deadline) {
+ return &timeoutError{}
+ }
+ }
+ }
+}
+
+type timeoutError struct{}
+
+func (e *timeoutError) Error() string {
+ return "timed out waiting for condition to stabilize"
+}
diff --git a/cache/ready.go b/cache/ready.go
new file mode 100644
index 000000000000..820d7fff3386
--- /dev/null
+++ b/cache/ready.go
@@ -0,0 +1,83 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "sync"
+)
+
+// ready tracks readiness state changes and allows callers to wait for a target state
+type ready struct {
+ mu sync.Mutex
+ isReady bool
+ stateCh chan struct{} // closed on any state transition, then replaced immediately
+}
+
+func newReady() *ready {
+ return &ready{stateCh: make(chan struct{})}
+}
+
+func (r *ready) Ready() bool {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.isReady
+}
+
+func (r *ready) WaitReady(ctx context.Context) error {
+ return r.waitForState(ctx, func() bool { return r.isReady })
+}
+
+func (r *ready) WaitNotReady(ctx context.Context) error {
+ return r.waitForState(ctx, func() bool { return !r.isReady })
+}
+
+func (r *ready) Set() {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if !r.isReady {
+ r.isReady = true
+ close(r.stateCh)
+ r.stateCh = make(chan struct{})
+ }
+}
+
+func (r *ready) Reset() {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if r.isReady {
+ r.isReady = false
+ close(r.stateCh)
+ r.stateCh = make(chan struct{})
+ }
+}
+
+func (r *ready) waitForState(ctx context.Context, pred func() bool) error {
+ for {
+ r.mu.Lock()
+ if pred() {
+ r.mu.Unlock()
+ return ctx.Err()
+ }
+ stateChCopy := r.stateCh
+ r.mu.Unlock()
+
+ select {
+ case <-stateChCopy:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
diff --git a/cache/ready_test.go b/cache/ready_test.go
new file mode 100644
index 000000000000..ae59caab240a
--- /dev/null
+++ b/cache/ready_test.go
@@ -0,0 +1,195 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "context"
+ "errors"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestWaitMethods(t *testing.T) {
+ tests := []struct {
+ name string
+ initialReady bool
+ expectReady bool
+ waitMethod string
+ expectBlock bool
+ }{
+ {
+ name: "not_ready_testing_WaitNotReady",
+ initialReady: false,
+ expectReady: false,
+ waitMethod: "not_ready",
+ expectBlock: false,
+ },
+ {
+ name: "not_ready_testing_WaitReady",
+ initialReady: false,
+ expectReady: false,
+ waitMethod: "ready",
+ expectBlock: true,
+ },
+ {
+ name: "ready_testing_WaitReady",
+ initialReady: true,
+ expectReady: true,
+ waitMethod: "ready",
+ expectBlock: false,
+ },
+ {
+ name: "ready_testing_WaitNotReady",
+ initialReady: true,
+ expectReady: true,
+ waitMethod: "not_ready",
+ expectBlock: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := newReady()
+
+ if tt.initialReady {
+ r.Set()
+ }
+
+ if got := r.Ready(); got != tt.expectReady {
+ t.Fatalf("Ready() = %t; want %t", got, tt.expectReady)
+ }
+
+ ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
+ defer cancel()
+
+ var err error
+ switch tt.waitMethod {
+ case "ready":
+ err = r.WaitReady(ctx)
+ case "not_ready":
+ err = r.WaitNotReady(ctx)
+ default:
+ t.Fatalf("invalid waitMethod: %s", tt.waitMethod)
+ }
+
+ if tt.expectBlock {
+ if !errors.Is(err, context.DeadlineExceeded) {
+ t.Fatalf("expected timeout but got: %v", err)
+ }
+ } else {
+ if err != nil {
+ t.Fatalf("expected immediate return but got error: %v", err)
+ }
+ }
+ })
+ }
+}
+
+func TestSetUnblocksWaiters(t *testing.T) {
+ testStateTransitionUnblocksWaiters(t, false, true, (*ready).Set, true, "WaitReady")
+}
+
+func TestResetUnblocksWaiters(t *testing.T) {
+ testStateTransitionUnblocksWaiters(t, true, false, (*ready).Reset, false, "WaitNotReady")
+}
+
+func testStateTransitionUnblocksWaiters(t *testing.T, initialSet bool, waitForReady bool, transition func(*ready), expectedReady bool, waitMethodName string) {
+ cases := []struct {
+ name string
+ n int
+ }{
+ {"one_waiter", 1},
+ {"several_waiters", 16},
+ {"many_waiters", 128},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ r := newReady()
+ if initialSet {
+ r.Set()
+ }
+
+ var startWg sync.WaitGroup
+ var readyWg sync.WaitGroup
+
+ errs := make(chan error, tc.n)
+ for i := 0; i < tc.n; i++ {
+ startWg.Add(1)
+ readyWg.Add(1)
+ go func() {
+ defer readyWg.Done()
+ startWg.Done()
+
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
+ defer cancel()
+
+ if waitForReady {
+ errs <- r.WaitReady(ctx)
+ } else {
+ errs <- r.WaitNotReady(ctx)
+ }
+ }()
+ }
+ startWg.Wait()
+ time.Sleep(50 * time.Millisecond)
+ transition(r)
+ readyWg.Wait()
+
+ for i := 0; i < tc.n; i++ {
+ if err := <-errs; err != nil {
+ t.Fatalf("waiter %d: %s = %v; want: nil", i, waitMethodName, err)
+ }
+ }
+
+ if r.Ready() != expectedReady {
+ t.Fatalf("Ready() = %t after transition; want %t", r.Ready(), expectedReady)
+ }
+
+ if waitForReady {
+ if err := r.WaitReady(t.Context()); err != nil {
+ t.Fatalf("immediate WaitReady() after transition = %v; want: nil", err)
+ }
+ } else {
+ if err := r.WaitNotReady(t.Context()); err != nil {
+ t.Fatalf("immediate WaitNotReady() after transition = %v; want: nil", err)
+ }
+ }
+ })
+ }
+}
+
+func TestIdempotentStateTransitions(t *testing.T) {
+ r := newReady()
+
+ r.Set()
+ r.Set()
+ if !r.Ready() {
+ t.Fatalf("Ready() = false after double Set(); want: true")
+ }
+ if err := r.WaitReady(t.Context()); err != nil {
+ t.Fatalf("WaitReady() after double Set() = %v; want: nil", err)
+ }
+
+ r.Reset()
+ r.Reset()
+ if r.Ready() {
+ t.Fatalf("Ready() = true after double Reset(); want: false")
+ }
+ if err := r.WaitNotReady(t.Context()); err != nil {
+ t.Fatalf("WaitNotReady() after double Reset() = %v; want ", err)
+ }
+}
diff --git a/cache/ringbuffer.go b/cache/ringbuffer.go
new file mode 100644
index 000000000000..b3ebafd79f69
--- /dev/null
+++ b/cache/ringbuffer.go
@@ -0,0 +1,156 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+type ringBuffer[T any] struct {
+ buffer []entry[T]
+ // head is the index immediately after the last non-empty entry in the buffer (i.e., the next write position).
+ head, tail, size int
+ revisionOf RevisionOf[T]
+}
+
+type entry[T any] struct {
+ revision int64
+ item T
+}
+
+type (
+ KeyPredicate = func([]byte) bool
+ RevisionOf[T any] func(T) int64
+ IterFunc[T any] func(rev int64, item T) bool
+)
+
+func newRingBuffer[T any](capacity int, revisionOf RevisionOf[T]) *ringBuffer[T] {
+ // assume capacity > 0 – validated by Cache
+ return &ringBuffer[T]{
+ buffer: make([]entry[T], capacity),
+ revisionOf: revisionOf,
+ }
+}
+
+func (r *ringBuffer[T]) Append(item T) {
+ entry := entry[T]{revision: r.revisionOf(item), item: item}
+ if r.full() {
+ r.tail = (r.tail + 1) % len(r.buffer)
+ } else {
+ r.size++
+ }
+ r.buffer[r.head] = entry
+ r.head = (r.head + 1) % len(r.buffer)
+}
+
+func (r *ringBuffer[T]) full() bool {
+ return r.size == len(r.buffer)
+}
+
+// AscendGreaterOrEqual iterates through entries in ascending order starting from the first entry with revision >= pivot.
+func (r *ringBuffer[T]) AscendGreaterOrEqual(pivot int64, iter IterFunc[T]) {
+ for i := r.findFirstIndexGreaterOrEqual(pivot); i < r.size; i++ {
+ entry := r.at(i)
+ if !iter(entry.revision, entry.item) {
+ return
+ }
+ }
+}
+
+// AscendLessThan iterates in ascending order over entries with revision < pivot.
+func (r *ringBuffer[T]) AscendLessThan(pivot int64, iter IterFunc[T]) {
+ for i := 0; i < r.findFirstIndexGreaterOrEqual(pivot); i++ {
+ entry := r.at(i)
+ if !iter(entry.revision, entry.item) {
+ return
+ }
+ }
+}
+
+// DescendGreaterThan iterates in descending order over entries with revision > pivot.
+func (r *ringBuffer[T]) DescendGreaterThan(pivot int64, iter IterFunc[T]) {
+ for i := r.size - 1; i > r.findLastIndexLessOrEqual(pivot); i-- {
+ entry := r.at(i)
+ if !iter(entry.revision, entry.item) {
+ return
+ }
+ }
+}
+
+// DescendLessOrEqual iterates in descending order over entries with revision <= pivot.
+func (r *ringBuffer[T]) DescendLessOrEqual(pivot int64, iter IterFunc[T]) {
+ for i := r.findLastIndexLessOrEqual(pivot); i >= 0; i-- {
+ entry := r.at(i)
+ if !iter(entry.revision, entry.item) {
+ return
+ }
+ }
+}
+
+// PeekLatest returns the most recently-appended revision (or 0 if empty).
+func (r *ringBuffer[T]) PeekLatest() int64 {
+ if r.size == 0 {
+ return 0
+ }
+ idx := (r.head - 1 + len(r.buffer)) % len(r.buffer)
+ return r.buffer[idx].revision
+}
+
+// PeekOldest returns the oldest revision currently stored (or 0 if empty).
+func (r *ringBuffer[T]) PeekOldest() int64 {
+ if r.size == 0 {
+ return 0
+ }
+ return r.buffer[r.tail].revision
+}
+
+func (r *ringBuffer[T]) RebaseHistory() {
+ r.head, r.tail, r.size = 0, 0, 0
+ for i := range r.buffer {
+ r.buffer[i] = entry[T]{}
+ }
+}
+
+func (r *ringBuffer[T]) moduloIndex(index int) int {
+ return (index + len(r.buffer)) % len(r.buffer)
+}
+
+func (r *ringBuffer[T]) at(logicalIndex int) entry[T] {
+ return r.buffer[r.moduloIndex(r.tail+logicalIndex)]
+}
+
+func (r *ringBuffer[T]) findFirstIndexGreaterOrEqual(pivot int64) int {
+ left, right := 0, r.size-1
+ for left <= right {
+ // Prevent overflow; see https://github.com/golang/go/blob/master/src/sort/search.go#L105.
+ mid := int(uint(left+right) >> 1)
+ if r.at(mid).revision >= pivot {
+ right = mid - 1
+ } else {
+ left = mid + 1
+ }
+ }
+ return left
+}
+
+func (r *ringBuffer[T]) findLastIndexLessOrEqual(pivot int64) int {
+ left, right := 0, r.size-1
+ for left <= right {
+ // Prevent overflow; see https://github.com/golang/go/blob/master/src/sort/search.go#L105.
+ mid := int(uint(left+right) >> 1)
+ if r.at(mid).revision <= pivot {
+ left = mid + 1
+ } else {
+ right = mid - 1
+ }
+ }
+ return right
+}
diff --git a/cache/ringbuffer_test.go b/cache/ringbuffer_test.go
new file mode 100644
index 000000000000..a5671eaea314
--- /dev/null
+++ b/cache/ringbuffer_test.go
@@ -0,0 +1,577 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+func TestPeekLatestAndOldest(t *testing.T) {
+ tests := []struct {
+ name string
+ capacity int
+ revs []int64
+ wantLatestRev int64
+ wantOldestRev int64
+ }{
+ {
+ name: "empty_buffer",
+ capacity: 4,
+ revs: nil,
+ wantLatestRev: 0,
+ wantOldestRev: 0,
+ },
+ {
+ name: "single_element",
+ capacity: 8,
+ revs: []int64{1},
+ wantLatestRev: 1,
+ wantOldestRev: 1,
+ },
+ {
+ name: "ascending_fill",
+ capacity: 4,
+ revs: []int64{1, 2, 3, 4},
+ wantLatestRev: 4,
+ wantOldestRev: 1,
+ },
+ {
+ name: "overwrite_when_full",
+ capacity: 3,
+ revs: []int64{5, 6, 7, 8},
+ wantLatestRev: 8,
+ wantOldestRev: 6,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+ for _, r := range tt.revs {
+ batch, err := makeEventBatch(r, "k", 1)
+ if err != nil {
+ t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", r, err)
+ }
+ rb.Append(batch)
+ }
+
+ latestRev := rb.PeekLatest()
+ oldestRev := rb.PeekOldest()
+
+ gotLatestRev := latestRev
+ gotOldestRev := oldestRev
+
+ if tt.wantLatestRev != gotLatestRev {
+ t.Fatalf("PeekLatest()=%d, want=%d", gotLatestRev, tt.wantLatestRev)
+ }
+ if tt.wantOldestRev != gotOldestRev {
+ t.Fatalf("PeekOldest()=%d, want=%d", gotOldestRev, tt.wantOldestRev)
+ }
+ })
+ }
+}
+
+func TestIterationMethods(t *testing.T) {
+ type iterTestCase struct {
+ method iterMethod
+ pivot int64
+ wantIterRevisions []int64
+ }
+ tests := []struct {
+ name string
+ capacity int
+ setupRevisions []int64
+ cases []iterTestCase
+ }{
+ {
+ name: "empty_buffer",
+ capacity: 4,
+ setupRevisions: nil,
+ cases: []iterTestCase{
+ {ascendGTE, 0, []int64{}},
+ {ascendLT, 10, []int64{}},
+ {descendGT, 0, []int64{}},
+ {descendLTE, 10, []int64{}},
+ },
+ },
+ {
+ name: "basic_filtering",
+ capacity: 5,
+ setupRevisions: []int64{1, 2, 3},
+ cases: []iterTestCase{
+ {ascendGTE, 0, []int64{1, 2, 3}},
+ {ascendGTE, 2, []int64{2, 3}},
+ {ascendGTE, 100, []int64{}},
+ {ascendLT, 3, []int64{1, 2}},
+ {ascendLT, 1, []int64{}},
+ {ascendLT, 100, []int64{1, 2, 3}},
+ {descendGT, 1, []int64{3, 2}},
+ {descendGT, 3, []int64{}},
+ {descendGT, 0, []int64{3, 2, 1}},
+ {descendLTE, 2, []int64{2, 1}},
+ {descendLTE, 3, []int64{3, 2, 1}},
+ {descendLTE, 0, []int64{}},
+ },
+ },
+ {
+ name: "overflowed stores only entries within capacity",
+ capacity: 3,
+ setupRevisions: []int64{20, 21, 22, 23, 24}, // stored: 22, 23, 24
+ cases: []iterTestCase{
+ {ascendGTE, 23, []int64{23, 24}},
+ {ascendGTE, 0, []int64{22, 23, 24}},
+ {ascendLT, 23, []int64{22}},
+ {ascendLT, 25, []int64{22, 23, 24}},
+ {descendGT, 22, []int64{24, 23}},
+ {descendGT, 25, []int64{}},
+ {descendLTE, 23, []int64{23, 22}},
+ {descendLTE, 24, []int64{24, 23, 22}},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ rb := setupRingBuffer(t, tt.capacity, tt.setupRevisions)
+
+ for _, tc := range tt.cases {
+ tc := tc
+ t.Run(fmt.Sprintf("%s_pivot_%d", tc.method, tc.pivot), func(t *testing.T) {
+ got := collectRevisions(rb, tc.method, tc.pivot)
+ if diff := cmp.Diff(tc.wantIterRevisions, got, protocmp.Transform()); diff != "" {
+ t.Fatalf("%s(%d) mismatch (-want +got):\n%s", tc.method, tc.pivot, diff)
+ }
+ })
+ }
+ })
+ }
+}
+
+func TestIterationWithBatching(t *testing.T) {
+ rb := newRingBuffer(6, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+ batchA := []*clientv3.Event{
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
+ }
+ batchB := []*clientv3.Event{
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
+ }
+ batchC := []*clientv3.Event{
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
+ }
+ rb.Append(batchA)
+ rb.Append(batchB)
+ rb.Append(batchC)
+
+ tests := []struct {
+ name string
+ method iterMethod
+ pivot int64
+ want [][]*clientv3.Event
+ }{
+ {
+ name: "ascending_gte_includes_batched_revision",
+ method: ascendGTE,
+ pivot: 10,
+ want: [][]*clientv3.Event{
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
+ },
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
+ },
+ },
+ },
+ {
+ name: "ascending_lt_stops_before_batched_revision",
+ method: ascendLT,
+ pivot: 10,
+ want: [][]*clientv3.Event{
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
+ },
+ },
+ },
+ {
+ name: "all_revisions_with_proper_batch_sizes",
+ method: ascendGTE,
+ pivot: 0,
+ want: [][]*clientv3.Event{
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-a"), ModRevision: 5}},
+ },
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-1"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-2"), ModRevision: 10}},
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-b-3"), ModRevision: 10}},
+ },
+ {
+ {Kv: &mvccpb.KeyValue{Key: []byte("key-c"), ModRevision: 12}},
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ var got [][]*clientv3.Event
+
+ rb.iterate(tt.method, tt.pivot, func(rev int64, events []*clientv3.Event) bool {
+ got = append(got, events)
+ return true
+ })
+
+ if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" {
+ t.Fatalf("Events mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestIterationEarlyStop(t *testing.T) {
+ rb := setupRingBuffer(t, 5, []int64{5, 10, 15, 20})
+ tests := []struct {
+ name string
+ method iterMethod
+ pivot int64
+ stopAfter int
+ want []int64
+ }{
+ {
+ name: "find_first_match_ascending",
+ method: ascendGTE,
+ pivot: 10,
+ stopAfter: 1,
+ want: []int64{10},
+ },
+ {
+ name: "find_first_two_ascending_lt",
+ method: ascendLT,
+ pivot: 20,
+ stopAfter: 2,
+ want: []int64{5, 10},
+ },
+ {
+ name: "find_first_two_descending_gt",
+ method: descendGT,
+ pivot: 5,
+ stopAfter: 2,
+ want: []int64{20, 15},
+ },
+ {
+ name: "find_first_match_descending_lte",
+ method: descendLTE,
+ pivot: 15,
+ stopAfter: 1,
+ want: []int64{15},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ var collected []int64
+ callCount := 0
+
+ rb.iterate(tt.method, tt.pivot, func(rev int64, events []*clientv3.Event) bool {
+ collected = append(collected, rev)
+ callCount++
+
+ shouldContinue := callCount < tt.stopAfter
+
+ if !shouldContinue {
+ t.Logf("Stopping early after %d items (callback returned false)", callCount)
+ }
+
+ return shouldContinue
+ })
+
+ if diff := cmp.Diff(tt.want, collected, protocmp.Transform()); diff != "" {
+ t.Fatalf("Early stop failed.\nExpected: \nDiff (-want +got):\n%s", diff)
+ }
+
+ if callCount != tt.stopAfter {
+ t.Fatalf("Expected exactly %d callback calls, got %d", tt.stopAfter, callCount)
+ }
+
+ t.Logf("Successfully stopped early: collected %v after %d callbacks",
+ collected, callCount)
+ })
+ }
+}
+
+type iterMethod string
+
+const (
+ ascendGTE iterMethod = "AscendGreaterOrEqual"
+ ascendLT iterMethod = "AscendLessThan"
+ descendGT iterMethod = "DescendGreaterThan"
+ descendLTE iterMethod = "DescendLessOrEqual"
+)
+
+func (r *ringBuffer[T]) iterate(method iterMethod, pivot int64, fn IterFunc[T]) {
+ switch method {
+ case ascendGTE:
+ r.AscendGreaterOrEqual(pivot, fn)
+ case ascendLT:
+ r.AscendLessThan(pivot, fn)
+ case descendGT:
+ r.DescendGreaterThan(pivot, fn)
+ case descendLTE:
+ r.DescendLessOrEqual(pivot, fn)
+ default:
+ panic(fmt.Sprintf("unknown iteration method: %s", method))
+ }
+}
+
+func TestAtomicOrdered(t *testing.T) {
+ tests := []struct {
+ name string
+ capacity int
+ inputs []struct {
+ rev int64
+ key string
+ size int
+ }
+ wantRev []int64
+ wantSize []int
+ }{
+ {
+ name: "unfiltered",
+ capacity: 5,
+ inputs: []struct {
+ rev int64
+ key string
+ size int
+ }{
+ {5, "a", 1},
+ {10, "b", 3},
+ {15, "c", 7},
+ {20, "d", 11},
+ },
+ wantRev: []int64{5, 10, 15, 20},
+ wantSize: []int{1, 3, 7, 11},
+ },
+ {
+ name: "across_wrap",
+ capacity: 3,
+ inputs: []struct {
+ rev int64
+ key string
+ size int
+ }{
+ {1, "a", 2},
+ {2, "b", 1},
+ {3, "c", 3},
+ {4, "d", 7},
+ },
+ wantRev: []int64{2, 3, 4},
+ wantSize: []int{1, 3, 7},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+ for _, in := range tt.inputs {
+ batch, err := makeEventBatch(in.rev, in.key, in.size)
+ if err != nil {
+ t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", in.rev, err)
+ }
+ rb.Append(batch)
+ }
+
+ gotRevs := []int64{}
+ var gotSizes []int
+ rb.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
+ gotRevs = append(gotRevs, rev)
+ gotSizes = append(gotSizes, len(events))
+ return true
+ })
+
+ if len(gotRevs) != len(tt.wantRev) {
+ t.Fatalf("len(got) = %d, want %d", len(gotRevs), len(tt.wantRev))
+ }
+ for i := range gotRevs {
+ if gotRevs[i] != tt.wantRev[i] {
+ t.Errorf("at idx %d: rev = %d, want %d", i, gotRevs[i], tt.wantRev[i])
+ }
+ if gotSizes[i] != tt.wantSize[i] {
+ t.Errorf("at rev %d: events.len = %d, want %d", gotRevs[i], gotSizes[i], tt.wantSize[i])
+ }
+ }
+ })
+ }
+}
+
+func TestRebaseHistory(t *testing.T) {
+ tests := []struct {
+ name string
+ revs []int64
+ }{
+ {
+ name: "rebase_empty_buffer",
+ revs: nil,
+ },
+ {
+ name: "rebase_after_data",
+ revs: []int64{7, 8, 9},
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ rb := newRingBuffer(4, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+ for _, r := range tt.revs {
+ batch, err := makeEventBatch(r, "k", 1)
+ if err != nil {
+ t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", r, err)
+ }
+ rb.Append(batch)
+ }
+
+ rb.RebaseHistory()
+
+ oldestRev := rb.PeekOldest()
+ latestRev := rb.PeekLatest()
+
+ if oldestRev != 0 {
+ t.Fatalf("PeekOldest()=%d, want=%d", oldestRev, 0)
+ }
+ if latestRev != 0 {
+ t.Fatalf("PeekLatest()=%d, want=%d", latestRev, 0)
+ }
+
+ gotRevs := []int64{}
+ rb.AscendGreaterOrEqual(0, func(rev int64, events []*clientv3.Event) bool {
+ gotRevs = append(gotRevs, rev)
+ return true
+ })
+
+ if len(gotRevs) != 0 {
+ t.Fatalf("AscendGreaterOrEqual() len(events)=%d, want=%d", len(gotRevs), 0)
+ }
+ })
+ }
+}
+
+func TestFull(t *testing.T) {
+ tests := []struct {
+ name string
+ capacity int
+ numAppends int
+ expectedFull bool
+ }{
+ {
+ name: "empty_buffer",
+ capacity: 3,
+ numAppends: 0,
+ expectedFull: false,
+ },
+ {
+ name: "partially_filled",
+ capacity: 5,
+ numAppends: 3,
+ expectedFull: false,
+ },
+ {
+ name: "exactly_at_capacity",
+ capacity: 3,
+ numAppends: 3,
+ expectedFull: true,
+ },
+ {
+ name: "beyond_capacity_wrapping",
+ capacity: 3,
+ numAppends: 5,
+ expectedFull: true,
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ rb := newRingBuffer(tt.capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+
+ for i := 1; i <= tt.numAppends; i++ {
+ batch, err := makeEventBatch(int64(i), "k", 1)
+ if err != nil {
+ t.Fatalf("makeEventBatch(%d, k, 1) failed: %v", i, err)
+ }
+ rb.Append(batch)
+ }
+
+ if got := rb.full(); got != tt.expectedFull {
+ t.Fatalf("full()=%t, want=%t (capacity=%d, appends=%d)",
+ got, tt.expectedFull, tt.capacity, tt.numAppends)
+ }
+ })
+ }
+}
+
+func setupRingBuffer(t *testing.T, capacity int, revs []int64) *ringBuffer[[]*clientv3.Event] {
+ rb := newRingBuffer(capacity, func(batch []*clientv3.Event) int64 { return batch[0].Kv.ModRevision })
+ for _, r := range revs {
+ batch, err := makeEventBatch(r, "key", 1)
+ if err != nil {
+ t.Fatalf("makeEventBatch(%d, %s, %d) failed: %v", r, "key", 1, err)
+ }
+ rb.Append(batch)
+ }
+ return rb
+}
+
+func collectRevisions(rb *ringBuffer[[]*clientv3.Event], method iterMethod, pivot int64) []int64 {
+ revs := []int64{}
+ rb.iterate(method, pivot, func(rev int64, events []*clientv3.Event) bool {
+ revs = append(revs, rev)
+ return true
+ })
+ return revs
+}
+
+func makeEventBatch(rev int64, key string, batchSize int) ([]*clientv3.Event, error) {
+ if batchSize < 0 {
+ return nil, fmt.Errorf("invalid batchSize %d", batchSize)
+ }
+ events := make([]*clientv3.Event, batchSize)
+ for i := range events {
+ events[i] = &clientv3.Event{
+ Kv: &mvccpb.KeyValue{
+ Key: []byte(fmt.Sprintf("%s-%d", key, i)),
+ ModRevision: rev,
+ },
+ }
+ }
+ return events, nil
+}
diff --git a/cache/snapshot.go b/cache/snapshot.go
new file mode 100644
index 000000000000..77f08136e651
--- /dev/null
+++ b/cache/snapshot.go
@@ -0,0 +1,62 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "k8s.io/utils/third_party/forked/golang/btree"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
+)
+
+// snapshot captures a full, point-in-time view of the KV state at rev.
+type snapshot struct {
+ rev int64
+ tree *btree.BTree[*kvItem]
+}
+
+func newClonedSnapshot(rev int64, t *btree.BTree[*kvItem]) *snapshot {
+ return &snapshot{rev: rev, tree: t.Clone()}
+}
+
+func (s *snapshot) Range(startKey, endKey []byte) []*mvccpb.KeyValue {
+ var out []*mvccpb.KeyValue
+ switch {
+ case len(endKey) == 0:
+ if item, ok := s.tree.Get(probeItemFromKey(startKey)); ok {
+ out = append(out, item.kv)
+ }
+ case isPrefixScan(endKey):
+ s.tree.AscendGreaterOrEqual(probeItemFromKey(startKey), func(item *kvItem) bool {
+ out = append(out, item.kv)
+ return true
+ })
+ default:
+ s.tree.AscendRange(
+ probeItemFromKey(startKey),
+ probeItemFromKey(endKey),
+ func(item *kvItem) bool {
+ out = append(out, item.kv)
+ return true
+ },
+ )
+ }
+ return out
+}
+
+func isPrefixScan(endKey []byte) bool {
+ return len(endKey) == 1 && endKey[0] == 0
+}
+
+func probeItemFromKey(key []byte) *kvItem { return &kvItem{key: string(key)} }
diff --git a/cache/store.go b/cache/store.go
new file mode 100644
index 000000000000..a4e91e21f46b
--- /dev/null
+++ b/cache/store.go
@@ -0,0 +1,199 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+
+ "k8s.io/utils/third_party/forked/golang/btree"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+var ErrNotReady = fmt.Errorf("cache: store not ready")
+
+// The store keeps a bounded history of snapshots using ringBuffer so that
+// reads at historical revisions can be served until they fall out of the window.
+type store struct {
+ mu sync.RWMutex
+ revCond *sync.Cond // revCond is broadcast whenever latest.rev changes
+ degree int
+ latest snapshot // latest is the mutable working snapshot
+ history ringBuffer[*snapshot] // history stores immutable cloned snapshots
+}
+
+func newStore(degree int, historyCapacity int) *store {
+ tree := btree.New[*kvItem](degree, kvItemLess)
+ s := &store{
+ degree: degree,
+ latest: snapshot{rev: 0, tree: tree},
+ history: *newRingBuffer(historyCapacity, func(s *snapshot) int64 { return s.rev }),
+ }
+ // Use RLocker so waiters hold read lock, allowing concurrent reads and non-blocking writes
+ s.revCond = sync.NewCond(s.mu.RLocker())
+ return s
+}
+
+type kvItem struct {
+ key string
+ kv *mvccpb.KeyValue
+}
+
+func newKVItem(kv *mvccpb.KeyValue) *kvItem {
+ return &kvItem{key: string(kv.Key), kv: kv}
+}
+
+func kvItemLess(a, b *kvItem) bool {
+ return a.key < b.key
+}
+
+func (s *store) Get(startKey, endKey []byte, rev int64) ([]*mvccpb.KeyValue, int64, error) {
+ snapshot, latestRev, err := s.getSnapshot(rev)
+ if err != nil {
+ return nil, 0, err
+ }
+ return snapshot.Range(startKey, endKey), latestRev, nil
+}
+
+func (s *store) getSnapshot(rev int64) (*snapshot, int64, error) {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ if s.latest.rev == 0 {
+ return nil, 0, ErrNotReady
+ }
+ if rev < 0 {
+ return nil, 0, fmt.Errorf("invalid revision: %d", rev)
+ }
+ if rev == 0 {
+ rev = s.latest.rev
+ }
+ if rev > s.latest.rev {
+ return nil, 0, rpctypes.ErrFutureRev
+ }
+
+ var targetSnapshot *snapshot
+ s.history.DescendLessOrEqual(rev, func(rev int64, snap *snapshot) bool {
+ targetSnapshot = snap
+ return false
+ })
+
+ if targetSnapshot == nil {
+ return nil, 0, rpctypes.ErrCompacted
+ }
+
+ return targetSnapshot, s.latest.rev, nil
+}
+
+// Restore replaces state with the bootstrap snapshot and resets history.
+func (s *store) Restore(kvs []*mvccpb.KeyValue, rev int64) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.latest.tree = btree.New[*kvItem](s.degree, kvItemLess)
+ for _, kv := range kvs {
+ s.latest.tree.ReplaceOrInsert(newKVItem(kv))
+ }
+ s.history.RebaseHistory()
+ s.latest.rev = rev
+ s.history.Append(newClonedSnapshot(rev, s.latest.tree))
+ s.revCond.Broadcast()
+}
+
+func (s *store) Apply(resp clientv3.WatchResponse) error {
+ if resp.Canceled {
+ return errors.New("canceled")
+ }
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if err := validateRevisions(resp, s.latest.rev); err != nil {
+ return err
+ }
+
+ switch {
+ case resp.IsProgressNotify():
+ s.applyProgressNotifyLocked(resp.Header.Revision)
+ return nil
+ case len(resp.Events) != 0:
+ return s.applyEventsLocked(resp.Events)
+ default:
+ return nil
+ }
+}
+
+func (s *store) applyProgressNotifyLocked(revision int64) {
+ if s.latest.rev == 0 {
+ return
+ }
+ s.latest.rev = revision
+ s.revCond.Broadcast()
+}
+
+func (s *store) applyEventsLocked(events []*clientv3.Event) error {
+ for i := 0; i < len(events); {
+ rev := events[i].Kv.ModRevision
+
+ for i < len(events) && events[i].Kv.ModRevision == rev {
+ ev := events[i]
+ switch ev.Type {
+ case clientv3.EventTypeDelete:
+ if _, ok := s.latest.tree.Delete(&kvItem{key: string(ev.Kv.Key)}); !ok {
+ return fmt.Errorf("cache: delete non-existent key %s", string(ev.Kv.Key))
+ }
+ case clientv3.EventTypePut:
+ s.latest.tree.ReplaceOrInsert(newKVItem(ev.Kv))
+ }
+ i++
+ }
+ s.latest.rev = rev
+ s.history.Append(newClonedSnapshot(rev, s.latest.tree))
+ s.revCond.Broadcast()
+ }
+ return nil
+}
+
+func (s *store) LatestRev() int64 {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.latest.rev
+}
+
+func validateRevisions(resp clientv3.WatchResponse, latestRev int64) error {
+ if resp.IsProgressNotify() {
+ if resp.Header.Revision < latestRev {
+ return fmt.Errorf("cache: progress notification out of order (progress %d < latest %d)", resp.Header.Revision, latestRev)
+ }
+ return nil
+ }
+ events := resp.Events
+ if len(events) == 0 {
+ return nil
+ }
+ for _, ev := range events {
+ r := ev.Kv.ModRevision
+ if r < latestRev {
+ return fmt.Errorf("cache: stale event batch (rev %d < latest %d)", r, latestRev)
+ }
+ if r == latestRev {
+ return fmt.Errorf("cache: duplicate revision batch breaks atomic guarantee (rev %d == latest %d)", r, latestRev)
+ }
+ }
+ return nil
+}
diff --git a/cache/store_test.go b/cache/store_test.go
new file mode 100644
index 000000000000..1d60be37f6aa
--- /dev/null
+++ b/cache/store_test.go
@@ -0,0 +1,440 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "errors"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
+
+ mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+func TestStoreGet(t *testing.T) {
+ tests := []struct {
+ name string
+ initialKVs []*mvccpb.KeyValue
+ initialRev int64
+
+ start []byte
+ end []byte
+
+ expectedKVs []*mvccpb.KeyValue
+ expectedRev int64
+ expectedErr error
+ }{
+ {
+ name: "empty_store_returns_ErrNotReady",
+ initialKVs: nil,
+ start: []byte("a"),
+ expectedErr: ErrNotReady,
+ },
+ {
+ name: "Get_single_key_hit",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5), makeKV("/a", "1", 5), makeKV("/c", "3", 5)},
+ initialRev: 5,
+ start: []byte("/b"),
+ expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5)},
+ expectedRev: 5,
+ },
+ {
+ name: "Get_single_key_miss_returns_empty",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 5), makeKV("/a", "1", 5), makeKV("/c", "3", 5)},
+ initialRev: 5,
+ start: []byte("/zzz"),
+ expectedKVs: nil,
+ expectedRev: 5,
+ },
+ {
+ name: "Get_explicit_range",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 10), makeKV("/b", "2", 10), makeKV("/c", "3", 10), makeKV("/d", "4", 10)},
+ initialRev: 10,
+ start: []byte("/b"),
+ end: []byte("/d"),
+ expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 10), makeKV("/c", "3", 10)},
+ expectedRev: 10,
+ },
+ {
+ name: "Get_range_includes_prefix_excludes_end",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 4), makeKV("/aa", "2", 4), makeKV("/ab", "3", 4), makeKV("/b", "4", 4)},
+ initialRev: 4,
+ start: []byte("/a"),
+ end: []byte("/b"),
+ expectedKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 4), makeKV("/aa", "2", 4), makeKV("/ab", "3", 4)},
+ expectedRev: 4,
+ },
+ {
+ name: "Get_empty_range_returns_empty",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 2), makeKV("/b", "2", 2)},
+ initialRev: 2,
+ start: []byte("/a"),
+ end: []byte("/a"),
+ expectedKVs: nil,
+ expectedRev: 2,
+ },
+ {
+ name: "Get_invalid_range_returns_empty",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 6), makeKV("/z", "9", 6)},
+ initialRev: 6,
+ start: []byte("/z"),
+ end: []byte("/a"),
+ expectedKVs: nil,
+ expectedRev: 6,
+ },
+ {
+ name: "Get_fromKey_scans_ordered",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 7), makeKV("/b", "2", 7), makeKV("/c", "3", 7), makeKV("/d", "4", 7)},
+ initialRev: 7,
+ start: []byte("/b"),
+ end: []byte{0},
+ expectedKVs: []*mvccpb.KeyValue{makeKV("/b", "2", 7), makeKV("/c", "3", 7), makeKV("/d", "4", 7)},
+ expectedRev: 7,
+ },
+ {
+ name: "Get_fromKey_with_no_results",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "1", 9), makeKV("/b", "2", 9)},
+ initialRev: 9,
+ start: []byte("/zzz"),
+ end: []byte{0},
+ expectedKVs: nil,
+ expectedRev: 9,
+ },
+ }
+
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ s := newStore(8, 32)
+ if test.initialKVs != nil {
+ s.Restore(test.initialKVs, test.initialRev)
+ }
+
+ kvs, rev, err := s.Get(test.start, test.end, 0)
+
+ if test.expectedErr != nil {
+ if !errors.Is(err, test.expectedErr) {
+ t.Fatalf("Get error = %v; want %v", err, test.expectedErr)
+ }
+ return
+ }
+ if err != nil {
+ t.Fatalf("Get returned unexpected error: %v", err)
+ }
+ if rev != test.expectedRev {
+ t.Fatalf("revision=%d; want %d", rev, test.expectedRev)
+ }
+
+ if diff := cmp.Diff(test.expectedKVs, kvs, protocmp.Transform()); diff != "" {
+ t.Fatalf("Get mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestStoreApply(t *testing.T) {
+ type testCase struct {
+ name string
+ initialKVs []*mvccpb.KeyValue
+ initialRev int64
+ eventBatches [][]*clientv3.Event
+
+ expectedLatestRev int64
+ expectedSnapshot []*mvccpb.KeyValue
+ expectErr bool
+ }
+ tests := []testCase{
+ {
+ name: "put_overwrites_key",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 10)},
+ initialRev: 10,
+ eventBatches: [][]*clientv3.Event{{makePutEvent("/k", "v2", 11)}},
+ expectedLatestRev: 11,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/k", "v2", 11)},
+ },
+ {
+ name: "put_contiguous_revision",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
+ initialRev: 20,
+ eventBatches: [][]*clientv3.Event{
+ {makePutEvent("/a", "A2", 21)},
+ {makePutEvent("/b", "B1", 22)},
+ {makePutEvent("/c", "C1", 23)},
+ },
+ expectedLatestRev: 23,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 21), makeKV("/b", "B1", 22), makeKV("/c", "C1", 23)},
+ },
+ {
+ name: "put_single_non_contiguous_batch",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
+ initialRev: 20,
+ eventBatches: [][]*clientv3.Event{{makePutEvent("/a", "A2", 25)}},
+ expectedLatestRev: 25,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 25)},
+ },
+ {
+ name: "put_multiple_non_contiguous_batches",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 21), makeKV("/b", "B1", 22)},
+ initialRev: 22,
+ eventBatches: [][]*clientv3.Event{
+ {makePutEvent("/a", "A2", 25)},
+ {makePutEvent("/b", "B2", 27)},
+ },
+ expectedLatestRev: 27,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A2", 25), makeKV("/b", "B2", 27)},
+ },
+ {
+ name: "apply_mixed_operations",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "A1", 20)},
+ initialRev: 20,
+ eventBatches: [][]*clientv3.Event{
+ {makePutEvent("/a", "A2", 21), makePutEvent("/b", "B1", 21), makePutEvent("/c", "C1", 21)},
+ {makePutEvent("/b", "B2", 22)},
+ {makeDelEvent("/c", 23), makePutEvent("/a", "A3", 23)},
+ {makePutEvent("/b", "B3", 24)},
+ },
+ expectedLatestRev: 24,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/a", "A3", 23), makeKV("/b", "B3", 24)},
+ },
+ {
+ name: "delete_same_key",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/a", "X", 10)},
+ initialRev: 10,
+ eventBatches: [][]*clientv3.Event{
+ {makeDelEvent("/a", 11)},
+ },
+ expectedLatestRev: 11,
+ expectedSnapshot: nil,
+ },
+ {
+ name: "delete_nonexistent_returns_error",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
+ initialRev: 5,
+ eventBatches: [][]*clientv3.Event{{makeDelEvent("/zzz", 6)}},
+ expectedLatestRev: 5,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
+ expectErr: true,
+ },
+ {
+ name: "mixed_delete_nonexistent_returns_error",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
+ initialRev: 5,
+ eventBatches: [][]*clientv3.Event{{makeDelEvent("/zzz", 6), makePutEvent("/r", "Y", 6)}},
+ expectedLatestRev: 5,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
+ expectErr: true,
+ },
+ {
+ name: "delete_then_delete_again_returns_error",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/p", "X", 5)},
+ initialRev: 5,
+ eventBatches: [][]*clientv3.Event{
+ {makeDelEvent("/p", 6)},
+ {makeDelEvent("/p", 7)},
+ },
+ expectedLatestRev: 6,
+ expectedSnapshot: nil,
+ expectErr: true,
+ },
+ {
+ name: "stale_batch_rejected",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
+ initialRev: 20,
+ eventBatches: [][]*clientv3.Event{{makePutEvent("/x", "2", 19)}},
+ expectedLatestRev: 20,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
+ expectErr: true,
+ },
+ {
+ name: "mixed_stale_batch_returns_error",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
+ initialRev: 20,
+ eventBatches: [][]*clientv3.Event{
+ {makePutEvent("/x", "should-not-apply", 19)},
+ {makeDelEvent("/x", 21), makePutEvent("/y", "new", 21)},
+ {makeDelEvent("/y", 22)},
+ },
+ expectedLatestRev: 20,
+ expectedSnapshot: []*mvccpb.KeyValue{makeKV("/x", "1", 20)},
+ expectErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ s := newStore(4, 32)
+ s.Restore(test.initialKVs, test.initialRev)
+
+ var gotErr error
+ for batchIndex, batch := range test.eventBatches {
+ resp := clientv3.WatchResponse{Events: batch}
+ if err := s.Apply(resp); err != nil {
+ gotErr = err
+ if !test.expectErr {
+ t.Fatalf("Apply(batch %d) unexpected error: %v", batchIndex, err)
+ }
+ break
+ }
+ }
+ if test.expectErr && gotErr == nil {
+ t.Fatalf("expected Apply() to error, but got nil")
+ }
+ if latest := s.LatestRev(); latest != test.expectedLatestRev {
+ t.Fatalf("LatestRev=%d; want %d", latest, test.expectedLatestRev)
+ }
+ verifyStoreSnapshot(t, s, test.expectedSnapshot, test.expectedLatestRev, 0)
+ })
+ }
+}
+
+func TestStoreRestore(t *testing.T) {
+ type restoreSeq struct {
+ kvs []*mvccpb.KeyValue
+ rev int64
+ }
+ tests := []struct {
+ name string
+ seq []restoreSeq
+ expectedSnap []*mvccpb.KeyValue
+ expectedRev int64
+ }{
+ {
+ name: "rebuilds_tree_and_resets_rev",
+ seq: []restoreSeq{
+ {[]*mvccpb.KeyValue{makeKV("/a", "1", 3), makeKV("/b", "2", 3)}, 3},
+ {[]*mvccpb.KeyValue{makeKV("/c", "3", 15)}, 15},
+ },
+ expectedSnap: []*mvccpb.KeyValue{makeKV("/c", "3", 15)},
+ expectedRev: 15,
+ },
+ {
+ name: "restore_to_revision_zero_returns_ErrNotReady",
+ seq: []restoreSeq{
+ {[]*mvccpb.KeyValue{makeKV("/a", "1", 5)}, 5},
+ {nil, 0},
+ },
+ expectedSnap: nil,
+ expectedRev: 0,
+ },
+ {
+ name: "restore_empty_ready",
+ seq: []restoreSeq{{nil, 5}},
+ expectedSnap: nil,
+ expectedRev: 5,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ s := newStore(8, 32)
+ for _, step := range tt.seq {
+ s.Restore(step.kvs, step.rev)
+ }
+ if tt.expectedRev == 0 {
+ if _, _, err := s.Get([]byte("/"), []byte{0}, 0); !errors.Is(err, ErrNotReady) {
+ t.Fatalf("Get after restore to rev=0 err=%v; want %v", err, ErrNotReady)
+ }
+ return
+ }
+ verifyStoreSnapshot(t, s, tt.expectedSnap, tt.expectedRev, 0)
+ })
+ }
+}
+
+func TestRestoreAppendCloneImmutability(t *testing.T) {
+ tests := []struct {
+ name string
+ initialKVs []*mvccpb.KeyValue
+ initialRev int64
+ events []*clientv3.Event
+ requestedRev int64
+ expectedSnap []*mvccpb.KeyValue
+ expectedLatestSnap []*mvccpb.KeyValue
+ expectedLatestRev int64
+ }{
+ {
+ name: "put_overwrites_key",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
+ initialRev: 5,
+ events: []*clientv3.Event{makePutEvent("/k", "v2", 6)},
+
+ requestedRev: 5,
+ expectedSnap: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
+ expectedLatestSnap: []*mvccpb.KeyValue{makeKV("/k", "v2", 6)},
+ expectedLatestRev: 6,
+ },
+ {
+ name: "delete_key",
+ initialKVs: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
+ initialRev: 5,
+ events: []*clientv3.Event{makeDelEvent("/k", 6)},
+
+ requestedRev: 5,
+ expectedSnap: []*mvccpb.KeyValue{makeKV("/k", "v1", 5)},
+ expectedLatestSnap: nil,
+ expectedLatestRev: 6,
+ },
+ }
+
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ s := newStore(8, 32)
+ if test.initialKVs != nil {
+ s.Restore(test.initialKVs, test.initialRev)
+ }
+ if len(test.events) > 0 {
+ resp := clientv3.WatchResponse{Events: test.events}
+ if err := s.Apply(resp); err != nil {
+ t.Fatalf("Apply failed: %v", err)
+ }
+ }
+
+ if test.requestedRev != 0 {
+ verifyStoreSnapshot(t, s, test.expectedSnap, test.expectedLatestRev, test.requestedRev)
+ }
+ verifyStoreSnapshot(t, s, test.expectedLatestSnap, test.expectedLatestRev, test.expectedLatestRev)
+ })
+ }
+}
+
+func makeKV(key, val string, rev int64) *mvccpb.KeyValue {
+ return &mvccpb.KeyValue{Key: []byte(key), Value: []byte(val), ModRevision: rev, CreateRevision: rev, Version: 1}
+}
+
+func makePutEvent(key, val string, rev int64) *clientv3.Event {
+ return &clientv3.Event{Type: clientv3.EventTypePut, Kv: &mvccpb.KeyValue{Key: []byte(key), Value: []byte(val), ModRevision: rev, CreateRevision: rev, Version: 1}}
+}
+
+func makeDelEvent(key string, rev int64) *clientv3.Event {
+ return &clientv3.Event{Type: clientv3.EventTypeDelete, Kv: &mvccpb.KeyValue{Key: []byte(key), ModRevision: rev}}
+}
+
+func verifyStoreSnapshot(t *testing.T, s *store, want []*mvccpb.KeyValue, wantRev int64, requestedRev int64) {
+ kvs, headerRev, err := s.Get([]byte("/"), []byte{0}, requestedRev)
+ if err != nil {
+ t.Fatalf("Get all keys (rev=%d): got error: %v", requestedRev, err)
+ }
+ latestRev := s.LatestRev()
+ if headerRev != latestRev {
+ t.Fatalf("header rev=%d; want latest %d (requestedRev=%d)", latestRev, wantRev, requestedRev)
+ }
+ if diff := cmp.Diff(want, kvs, protocmp.Transform()); diff != "" {
+ t.Fatalf("snapshot mismatch (requestedRev=%d) (-want +got):\n%s", requestedRev, diff)
+ }
+}
diff --git a/cache/watcher.go b/cache/watcher.go
new file mode 100644
index 000000000000..840398b8a202
--- /dev/null
+++ b/cache/watcher.go
@@ -0,0 +1,84 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 cache
+
+import (
+ "sync"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+// watcher holds one client’s buffered stream of events.
+type watcher struct {
+ respCh chan clientv3.WatchResponse
+ cancelResp *clientv3.WatchResponse
+ keyPred KeyPredicate
+ stopOnce sync.Once
+}
+
+func newWatcher(bufSize int, pred KeyPredicate) *watcher {
+ return &watcher{
+ respCh: make(chan clientv3.WatchResponse, bufSize),
+ keyPred: pred,
+ }
+}
+
+// true -> events delivered (or filtered/duplicate)
+// false -> buffer full (caller should mark watcher “lagging”)
+func (w *watcher) enqueueResponse(resp clientv3.WatchResponse) bool {
+ if !resp.IsProgressNotify() && w.keyPred != nil {
+ filtered := make([]*clientv3.Event, 0, len(resp.Events))
+ for _, event := range resp.Events {
+ if w.keyPred(event.Kv.Key) {
+ filtered = append(filtered, event)
+ }
+ }
+ if len(filtered) == 0 {
+ return true
+ }
+ resp.Events = filtered
+ }
+ if resp.Header == nil {
+ resp.Header = &pb.ResponseHeader{}
+ }
+ select {
+ case w.respCh <- resp:
+ return true
+ default:
+ return false
+ }
+}
+
+func (w *watcher) Compact(compactRev int64) {
+ resp := &clientv3.WatchResponse{
+ Header: &pb.ResponseHeader{},
+ Canceled: true,
+ CompactRevision: compactRev,
+ CancelReason: rpctypes.ErrCompacted.Error(),
+ }
+ w.stopOnce.Do(func() {
+ w.cancelResp = resp
+ close(w.respCh)
+ })
+}
+
+// Stop closes the event channel atomically.
+func (w *watcher) Stop() {
+ w.stopOnce.Do(func() {
+ close(w.respCh)
+ })
+}
diff --git a/client/pkg/.gomodguard.yaml b/client/pkg/.gomodguard.yaml
new file mode 100644
index 000000000000..83a99c219c1f
--- /dev/null
+++ b/client/pkg/.gomodguard.yaml
@@ -0,0 +1,15 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/api/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/pkg/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/server/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/client/pkg/LICENSE b/client/pkg/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/client/pkg/LICENSE
+++ b/client/pkg/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/client/pkg/fileutil/fileutil_test.go b/client/pkg/fileutil/fileutil_test.go
index 6abba29ca593..1c765e4e03c9 100644
--- a/client/pkg/fileutil/fileutil_test.go
+++ b/client/pkg/fileutil/fileutil_test.go
@@ -19,7 +19,6 @@ import (
"io"
"math/rand"
"os"
- "os/user"
"path/filepath"
"runtime"
"strings"
@@ -35,13 +34,7 @@ func TestIsDirWriteable(t *testing.T) {
tmpdir := t.TempDir()
require.NoErrorf(t, IsDirWriteable(tmpdir), "unexpected IsDirWriteable error")
require.NoErrorf(t, os.Chmod(tmpdir, 0o444), "unexpected os.Chmod error")
- me, err := user.Current()
- if err != nil {
- // err can be non-nil when cross compiled
- // http://stackoverflow.com/questions/20609415/cross-compiling-user-current-not-implemented-on-linux-amd64
- t.Skipf("failed to get current user: %v", err)
- }
- if me.Name == "root" || runtime.GOOS == "windows" {
+ if os.Getuid() == 0 || runtime.GOOS == "windows" {
// ideally we should check CAP_DAC_OVERRIDE.
// but it does not matter for tests.
// Chmod is not supported under windows.
diff --git a/client/pkg/fileutil/lock_test.go b/client/pkg/fileutil/lock_test.go
index 0c5da9855ad7..ea449e1cff3a 100644
--- a/client/pkg/fileutil/lock_test.go
+++ b/client/pkg/fileutil/lock_test.go
@@ -23,7 +23,7 @@ import (
)
func TestLockAndUnlock(t *testing.T) {
- f, err := os.CreateTemp("", "lock")
+ f, err := os.CreateTemp(t.TempDir(), "lock")
require.NoError(t, err)
f.Close()
defer func() {
diff --git a/client/pkg/go.mod b/client/pkg/go.mod
index e44fd2c27936..0e4fda082daa 100644
--- a/client/pkg/go.mod
+++ b/client/pkg/go.mod
@@ -1,20 +1,20 @@
module go.etcd.io/etcd/client/pkg/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
- github.com/coreos/go-systemd/v22 v22.5.0
+ github.com/coreos/go-systemd/v22 v22.7.0
github.com/stretchr/testify v1.11.1
- go.uber.org/zap v1.27.0
- golang.org/x/sys v0.41.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/sys v0.45.0
)
require (
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/kr/pretty v0.3.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/client/pkg/go.sum b/client/pkg/go.sum
index 6357dd96ea32..ca5d18481272 100644
--- a/client/pkg/go.sum
+++ b/client/pkg/go.sum
@@ -1,9 +1,8 @@
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -12,8 +11,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
@@ -23,10 +22,10 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/client/pkg/logutil/zap_test.go b/client/pkg/logutil/zap_test.go
index ac12e1aee88f..4ccee8bb7440 100644
--- a/client/pkg/logutil/zap_test.go
+++ b/client/pkg/logutil/zap_test.go
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/client/pkg/pathutil/path.go b/client/pkg/pathutil/path.go
index f26254ba933e..6e968922b42b 100644
--- a/client/pkg/pathutil/path.go
+++ b/client/pkg/pathutil/path.go
@@ -1,6 +1,16 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 pathutil implements utility functions for handling slash-separated
// paths.
diff --git a/client/pkg/testutil/before.go b/client/pkg/testutil/before.go
index 155a491ad405..6259fccfca19 100644
--- a/client/pkg/testutil/before.go
+++ b/client/pkg/testutil/before.go
@@ -19,9 +19,6 @@ import (
"os"
"testing"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
"go.etcd.io/etcd/client/pkg/v3/verify"
)
@@ -31,15 +28,12 @@ func BeforeTest(tb testing.TB) {
revertVerifyFunc := verify.EnableAllVerifications()
- path, err := os.Getwd()
- require.NoError(tb, err)
tempDir := tb.TempDir()
- require.NoError(tb, os.Chdir(tempDir))
+ tb.Chdir(tempDir)
tb.Logf("Changing working directory to: %s", tempDir)
tb.Cleanup(func() {
revertVerifyFunc()
- assert.NoError(tb, os.Chdir(path))
})
}
diff --git a/client/pkg/testutil/leak.go b/client/pkg/testutil/leak.go
index ce859ef530fa..61e7554edd7e 100644
--- a/client/pkg/testutil/leak.go
+++ b/client/pkg/testutil/leak.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 testutil
@@ -112,7 +122,7 @@ func RegisterLeakDetection(t TB) {
// It will detect common goroutine leaks, retrying in case there are goroutines
// not synchronously torn down, and fail the test if any goroutines are stuck.
func afterTest(t TB) {
- // If test-failed the leaked goroutines list is hidding the real
+ // If the test fails, the leaked goroutines list may hide the real
// source of problem.
if !t.Failed() {
if err := CheckAfterTest(1 * time.Second); err != nil {
diff --git a/client/pkg/testutil/testutil.go b/client/pkg/testutil/testutil.go
index 425c9f3b6b56..b5505b7dd52e 100644
--- a/client/pkg/testutil/testutil.go
+++ b/client/pkg/testutil/testutil.go
@@ -16,6 +16,8 @@
package testutil
import (
+ "flag"
+ "log"
"net/url"
"os"
"runtime"
@@ -100,10 +102,13 @@ func SkipTestIfShortMode(t TB, reason string) {
// ExitInShortMode closes the current process (with 0) if the short test mode detected.
//
// To be used in Test-main, where test context (testing.TB) is not available.
-//
-// Requires custom env-variable (GOLANG_TEST_SHORT) apart of `go test --short flag`.
func ExitInShortMode(reason string) {
- if os.Getenv("GOLANG_TEST_SHORT") == "true" {
+ // Calling testing.Short() requires flags to be parsed before.
+ if !flag.Parsed() {
+ flag.Parse()
+ }
+ if testing.Short() {
+ log.Println(reason)
os.Exit(0)
}
}
diff --git a/client/pkg/transport/listener.go b/client/pkg/transport/listener.go
index 9c2d29ba998e..659bb7adb17e 100644
--- a/client/pkg/transport/listener.go
+++ b/client/pkg/transport/listener.go
@@ -39,7 +39,7 @@ import (
"go.etcd.io/etcd/client/pkg/v3/verify"
)
-// NewListener creates a new listner.
+// NewListener creates a new listener.
func NewListener(addr, scheme string, tlsinfo *TLSInfo) (l net.Listener, err error) {
return newListener(addr, scheme, WithTLSInfo(tlsinfo))
}
diff --git a/client/pkg/transport/listener_opts.go b/client/pkg/transport/listener_opts.go
index 7536f6aff462..cec7484e72de 100644
--- a/client/pkg/transport/listener_opts.go
+++ b/client/pkg/transport/listener_opts.go
@@ -53,7 +53,7 @@ func (lo *ListenerOptions) IsSocketOpts() bool {
return lo.socketOpts.ReusePort || lo.socketOpts.ReuseAddress
}
-// IsTLS returns true if listner options includes TLSInfo.
+// IsTLS returns true if listener options includes TLSInfo.
func (lo *ListenerOptions) IsTLS() bool {
if lo.tlsInfo == nil {
return false
diff --git a/client/pkg/transport/listener_tls.go b/client/pkg/transport/listener_tls.go
index 2c94841625b0..63566e1f6429 100644
--- a/client/pkg/transport/listener_tls.go
+++ b/client/pkg/transport/listener_tls.go
@@ -166,6 +166,29 @@ func (l *tlsListener) acceptLoop() {
}
}
+// ConfigureCRLVerification appends a VerifyConnection hook to cfg that
+// rejects any peer certificate whose serial number appears in the CRL file
+// configured on info. It is a no-op when CRLFile is empty. Any existing
+// VerifyConnection hook is called first and its error short-circuits.
+func (info TLSInfo) ConfigureCRLVerification(cfg *tls.Config) {
+ if len(info.CRLFile) == 0 {
+ return
+ }
+ crlFile := info.CRLFile
+ prev := cfg.VerifyConnection
+ cfg.VerifyConnection = func(cs tls.ConnectionState) error {
+ if prev != nil {
+ if err := prev(cs); err != nil {
+ return err
+ }
+ }
+ if len(cs.PeerCertificates) == 0 {
+ return nil
+ }
+ return checkCRL(crlFile, cs.PeerCertificates)
+ }
+}
+
func checkCRL(crlPath string, cert []*x509.Certificate) error {
// TODO: cache
crlBytes, err := os.ReadFile(crlPath)
diff --git a/client/pkg/transport/timeout_transport.go b/client/pkg/transport/timeout_transport.go
index ea16b4c0f869..6b6bc67156d8 100644
--- a/client/pkg/transport/timeout_transport.go
+++ b/client/pkg/transport/timeout_transport.go
@@ -39,7 +39,7 @@ func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.
tr.MaxIdleConnsPerHost = 1024
}
- tr.Dial = (&rwTimeoutDialer{
+ tr.Dial = (&rwTimeoutDialer{ //nolint:staticcheck // TODO: remove for a supported version
Dialer: net.Dialer{
Timeout: dialtimeoutd,
KeepAlive: 30 * time.Second,
diff --git a/client/pkg/transport/timeout_transport_test.go b/client/pkg/transport/timeout_transport_test.go
index d5b4bcaf9c84..d95be5c95fd5 100644
--- a/client/pkg/transport/timeout_transport_test.go
+++ b/client/pkg/transport/timeout_transport_test.go
@@ -37,7 +37,7 @@ func TestNewTimeoutTransport(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(remoteAddr))
defer srv.Close()
- conn, err := tr.Dial("tcp", srv.Listener.Addr().String())
+ conn, err := tr.Dial("tcp", srv.Listener.Addr().String()) //nolint:staticcheck // TODO: remove for a supported version
require.NoError(t, err)
defer conn.Close()
diff --git a/client/pkg/types/set.go b/client/pkg/types/set.go
index 3e69c8d8b943..8c9d8773ef1b 100644
--- a/client/pkg/types/set.go
+++ b/client/pkg/types/set.go
@@ -23,15 +23,23 @@ import (
type Set interface {
Add(string)
Remove(string)
- Contains(string) bool
+ Contains(val ...string) bool
Equals(Set) bool
Length() int
Values() []string
Copy() Set
Sub(Set) Set
+
+ // ContainsAll returns whether the set contains all given values
+ // Deprecated: Use Contains instead.
+ ContainsAll(values []string) bool
+}
+
+type ThreadsafeSet interface {
+ Set
}
-func NewUnsafeSet(values ...string) *unsafeSet {
+func NewUnsafeSet(values ...string) Set {
set := &unsafeSet{make(map[string]struct{})}
for _, v := range values {
set.Add(v)
@@ -39,11 +47,13 @@ func NewUnsafeSet(values ...string) *unsafeSet {
return set
}
-func NewThreadsafeSet(values ...string) *tsafeSet {
+func NewThreadsafeSet(values ...string) ThreadsafeSet {
us := NewUnsafeSet(values...)
return &tsafeSet{us, sync.RWMutex{}}
}
+var _ Set = (*unsafeSet)(nil)
+
type unsafeSet struct {
d map[string]struct{}
}
@@ -59,12 +69,17 @@ func (us *unsafeSet) Remove(value string) {
}
// Contains returns whether the set contains the given value
-func (us *unsafeSet) Contains(value string) (exists bool) {
- _, exists = us.d[value]
- return exists
+func (us *unsafeSet) Contains(values ...string) (exists bool) {
+ for _, value := range values {
+ if _, exists := us.d[value]; !exists {
+ return false
+ }
+ }
+ return true
}
// ContainsAll returns whether the set contains all given values
+// Deprecated: Use Contains instead.
func (us *unsafeSet) ContainsAll(values []string) bool {
for _, s := range values {
if !us.Contains(s) {
@@ -122,8 +137,10 @@ func (us *unsafeSet) Sub(other Set) Set {
return result
}
+var _ ThreadsafeSet = (*tsafeSet)(nil)
+
type tsafeSet struct {
- us *unsafeSet
+ us Set
m sync.RWMutex
}
@@ -139,10 +156,18 @@ func (ts *tsafeSet) Remove(value string) {
ts.us.Remove(value)
}
-func (ts *tsafeSet) Contains(value string) (exists bool) {
+func (ts *tsafeSet) Contains(values ...string) (exists bool) {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ return ts.us.Contains(values...)
+}
+
+// ContainsAll returns whether the set contains all given values
+// Deprecated: Use Contains instead.
+func (ts *tsafeSet) ContainsAll(values []string) bool {
ts.m.RLock()
defer ts.m.RUnlock()
- return ts.us.Contains(value)
+ return ts.us.ContainsAll(values)
}
func (ts *tsafeSet) Equals(other Set) bool {
diff --git a/client/pkg/types/urls.go b/client/pkg/types/urls.go
index 49a38967e64d..7dca16c0482c 100644
--- a/client/pkg/types/urls.go
+++ b/client/pkg/types/urls.go
@@ -47,7 +47,6 @@ func NewURLs(strs []string) (URLs, error) {
return nil, fmt.Errorf("URL must not contain a path: %s", in)
}
case "unix", "unixs":
- break
default:
return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
}
diff --git a/client/pkg/verify/verify.go b/client/pkg/verify/verify.go
index a7b2097bed9b..cb48d8ff02c1 100644
--- a/client/pkg/verify/verify.go
+++ b/client/pkg/verify/verify.go
@@ -66,9 +66,18 @@ func DisableVerifications() func() {
// Verify performs verification if the assertions are enabled.
// In the default setup running in tests and skipped in the production code.
-func Verify(f func()) {
+func Verify(msg string, f VerifyFunc) {
if IsVerificationEnabled(envVerifyValueAssert) {
- f()
+ ok, details := f()
+ verifier(ok, msg, details)
+ }
+}
+
+type VerifyFunc func() (condition bool, details map[string]any)
+
+func verifier(condition bool, msg string, details map[string]any) {
+ if !condition {
+ panic(fmt.Sprintf("%s. details: %v.", msg, details))
}
}
diff --git a/client/v3/.gomodguard.yaml b/client/v3/.gomodguard.yaml
new file mode 100644
index 000000000000..138dedc4f724
--- /dev/null
+++ b/client/v3/.gomodguard.yaml
@@ -0,0 +1,13 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/pkg/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/server/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/client/v3/LICENSE b/client/v3/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/client/v3/LICENSE
+++ b/client/v3/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/client/v3/auth.go b/client/v3/auth.go
index 382172b21bf5..7d9a55f1d408 100644
--- a/client/v3/auth.go
+++ b/client/v3/auth.go
@@ -49,9 +49,9 @@ type (
)
const (
- PermRead = authpb.READ
- PermWrite = authpb.WRITE
- PermReadWrite = authpb.READWRITE
+ PermRead = authpb.Permission_READ
+ PermWrite = authpb.Permission_WRITE
+ PermReadWrite = authpb.Permission_READWRITE
)
type UserAddOptions authpb.UserAddOptions
diff --git a/client/v3/block_logger.go b/client/v3/block_logger.go
new file mode 100644
index 000000000000..a5812928431d
--- /dev/null
+++ b/client/v3/block_logger.go
@@ -0,0 +1,67 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 clientv3
+
+import (
+ "sync"
+ "time"
+)
+
+type blockLogger struct {
+ interval time.Duration
+ threshold time.Duration
+ now func() time.Time
+ logFunc func(eventCount int, timeWaiting time.Duration, window time.Duration)
+
+ mu sync.Mutex
+ lastLogTime time.Time
+ eventCount int
+ timeWaiting time.Duration
+}
+
+func newBlockLogger(interval time.Duration, threshold time.Duration, now func() time.Time, logFunc func(eventCount int, timeWaiting time.Duration, window time.Duration)) *blockLogger {
+ if now == nil {
+ now = time.Now
+ }
+ l := &blockLogger{
+ interval: interval,
+ threshold: threshold,
+ now: now,
+ logFunc: logFunc,
+ }
+ l.lastLogTime = l.now()
+ return l
+}
+
+func (l *blockLogger) recordWait(waitTime time.Duration) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+
+ l.eventCount++
+ l.timeWaiting += waitTime
+
+ now := l.now()
+ window := now.Sub(l.lastLogTime)
+ if window < l.interval {
+ return
+ }
+
+ if l.timeWaiting > l.threshold && l.logFunc != nil {
+ l.logFunc(l.eventCount, l.timeWaiting, window)
+ }
+ l.eventCount = 0
+ l.timeWaiting = 0
+ l.lastLogTime = now
+}
diff --git a/client/v3/block_logger_test.go b/client/v3/block_logger_test.go
new file mode 100644
index 000000000000..f9cc7ab6127f
--- /dev/null
+++ b/client/v3/block_logger_test.go
@@ -0,0 +1,102 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 clientv3
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestBlockLogger(t *testing.T) {
+ const (
+ logInterval = 5 * time.Second
+ logThreshold = 100 * time.Millisecond
+ )
+
+ type step struct {
+ advance time.Duration
+ wait time.Duration
+ }
+ type logEntry struct {
+ count int
+ wait time.Duration
+ window time.Duration
+ }
+
+ tests := []struct {
+ name string
+ steps []step
+ wantLogs []logEntry
+ }{
+ {
+ name: "does not log before interval elapses",
+ steps: []step{
+ {wait: logThreshold},
+ {advance: time.Second, wait: logThreshold},
+ },
+ },
+ {
+ name: "does not log when accumulated wait stays below threshold",
+ steps: []step{
+ {wait: 20 * time.Millisecond},
+ {advance: logInterval, wait: 20 * time.Millisecond},
+ },
+ },
+ {
+ name: "logs accumulated waits when interval elapses and threshold is exceeded",
+ steps: []step{
+ {wait: 40 * time.Millisecond},
+ {advance: logInterval, wait: 80 * time.Millisecond},
+ },
+ wantLogs: []logEntry{
+ {count: 2, wait: 120 * time.Millisecond, window: logInterval},
+ },
+ },
+ {
+ name: "logs once per accumulation window and resets after logging",
+ steps: []step{
+ {wait: 40 * time.Millisecond},
+ {advance: logInterval, wait: 80 * time.Millisecond},
+ {advance: logInterval, wait: 120 * time.Millisecond},
+ },
+ wantLogs: []logEntry{
+ {count: 2, wait: 120 * time.Millisecond, window: logInterval},
+ {count: 1, wait: 120 * time.Millisecond, window: logInterval},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ now := time.Unix(0, 0)
+ var logs []logEntry
+ logger := newBlockLogger(logInterval, logThreshold, func() time.Time {
+ return now
+ }, func(eventCount int, timeWaiting time.Duration, window time.Duration) {
+ logs = append(logs, logEntry{count: eventCount, wait: timeWaiting, window: window})
+ })
+
+ for _, step := range tt.steps {
+ now = now.Add(step.advance)
+ logger.recordWait(step.wait)
+ }
+
+ if !reflect.DeepEqual(logs, tt.wantLogs) {
+ t.Fatalf("logs = %+v, want %+v", logs, tt.wantLogs)
+ }
+ })
+ }
+}
diff --git a/client/v3/client.go b/client/v3/client.go
index 24f5988986d3..e0cb49d04e26 100644
--- a/client/v3/client.go
+++ b/client/v3/client.go
@@ -20,6 +20,7 @@ import (
"fmt"
"strings"
"sync"
+ "sync/atomic"
"time"
"github.com/coreos/go-semver/semver"
@@ -43,6 +44,7 @@ import (
var (
ErrNoAvailableEndpoints = errors.New("etcdclient: no available endpoints")
ErrOldCluster = errors.New("etcdclient: old cluster version")
+ ErrMutuallyExclusiveCfg = errors.New("Username/Password and Token configurations are mutually exclusive")
)
// Client provides and manages an etcd v3 client session.
@@ -69,13 +71,15 @@ type Client struct {
// Username is a user name for authentication.
Username string
// Password is a password for authentication.
- Password string
+ Password string
+ // Token is a JWT used for authentication instead of a password.
+ Token string
+
authTokenBundle credentials.PerRPCCredentialsBundle
callOpts []grpc.CallOption
- lgMu *sync.RWMutex
- lg *zap.Logger
+ lg atomic.Pointer[zap.Logger]
}
// New creates a new etcdv3 client from a given configuration.
@@ -92,12 +96,12 @@ func New(cfg Config) (*Client, error) {
// service interface implementations and do not need connection management.
func NewCtxClient(ctx context.Context, opts ...Option) *Client {
cctx, cancel := context.WithCancel(ctx)
- c := &Client{ctx: cctx, cancel: cancel, lgMu: new(sync.RWMutex), epMu: new(sync.RWMutex)}
+ c := &Client{ctx: cctx, cancel: cancel, epMu: new(sync.RWMutex)}
for _, opt := range opts {
opt(c)
}
- if c.lg == nil {
- c.lg = zap.NewNop()
+ if c.lg.Load() == nil {
+ c.lg.Store(zap.NewNop())
}
return c
}
@@ -118,7 +122,7 @@ func NewFromURLs(urls []string) (*Client, error) {
// WithZapLogger is a NewCtxClient option that overrides the logger
func WithZapLogger(lg *zap.Logger) Option {
return func(c *Client) {
- c.lg = lg
+ c.lg.Store(lg)
}
}
@@ -129,19 +133,14 @@ func WithZapLogger(lg *zap.Logger) Option {
// Does not changes grpcLogger, that can be explicitly configured
// using grpc_zap.ReplaceGrpcLoggerV2(..) method.
func (c *Client) WithLogger(lg *zap.Logger) *Client {
- c.lgMu.Lock()
- c.lg = lg
- c.lgMu.Unlock()
+ c.lg.Store(lg)
return c
}
// GetLogger gets the logger.
// NOTE: This method is for internal use of etcd-client library and should not be used as general-purpose logger.
func (c *Client) GetLogger() *zap.Logger {
- c.lgMu.RLock()
- l := c.lg
- c.lgMu.RUnlock()
- return l
+ return c.lg.Load()
}
// Close shuts down the client's etcd connections.
@@ -197,13 +196,11 @@ func (c *Client) Sync(ctx context.Context) error {
}
// The linearizable `MemberList` returned successfully, so the
// endpoints shouldn't be empty.
- verify.Verify(func() {
- if len(eps) == 0 {
- panic("empty endpoints returned from etcd cluster")
- }
+ verify.Verify("empty endpoints returned from etcd cluster", func() (bool, map[string]any) {
+ return len(eps) > 0, nil
})
c.SetEndpoints(eps...)
- c.lg.Debug("set etcd endpoints by autoSync", zap.Strings("endpoints", eps))
+ c.GetLogger().Debug("set etcd endpoints by autoSync", zap.Strings("endpoints", eps))
return nil
}
@@ -221,7 +218,7 @@ func (c *Client) autoSync() {
err := c.Sync(ctx)
cancel()
if err != nil && !errors.Is(err, c.ctx.Err()) {
- c.lg.Info("Auto sync endpoints failed.", zap.Error(err))
+ c.GetLogger().Info("Auto sync endpoints failed.", zap.Error(err))
}
}
}
@@ -288,6 +285,11 @@ func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
func (c *Client) getToken(ctx context.Context) error {
var err error // return last error in a case of fail
+ if c.Token != "" {
+ c.authTokenBundle.UpdateAuthToken(c.Token)
+ return nil
+ }
+
if c.Username == "" || c.Password == "" {
return nil
}
@@ -322,18 +324,8 @@ func (c *Client) dial(creds grpccredentials.TransportCredentials, dopts ...grpc.
opts = append(opts, c.cfg.DialOptions...)
- dctx := c.ctx
- if c.cfg.DialTimeout > 0 {
- var cancel context.CancelFunc
- dctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
- defer cancel() // TODO: Is this right for cases where grpc.WithBlock() is not set on the dial options?
- }
target := fmt.Sprintf("%s://%p/%s", resolver.Schema, c, authority(c.endpoints[0]))
- conn, err := grpc.DialContext(dctx, target, opts...)
- if err != nil {
- return nil, err
- }
- return conn, nil
+ return grpc.NewClient(target, opts...)
}
func authority(endpoint string) string {
@@ -376,6 +368,10 @@ func newClient(cfg *Config) (*Client, error) {
creds = credentials.NewTransportCredential(cfg.TLS)
}
+ if cfg.Token != "" && (cfg.Username != "" || cfg.Password != "") {
+ return nil, ErrMutuallyExclusiveCfg
+ }
+
// use a temporary skeleton client to bootstrap first connection
baseCtx := context.TODO()
if cfg.Context != nil {
@@ -391,29 +387,36 @@ func newClient(cfg *Config) (*Client, error) {
cancel: cancel,
epMu: new(sync.RWMutex),
callOpts: defaultCallOpts,
- lgMu: new(sync.RWMutex),
}
var err error
+ var lg *zap.Logger
if cfg.Logger != nil {
- client.lg = cfg.Logger
+ lg = cfg.Logger
} else if cfg.LogConfig != nil {
- client.lg, err = cfg.LogConfig.Build()
+ lg, err = cfg.LogConfig.Build()
} else {
- client.lg, err = logutil.CreateDefaultZapLogger(etcdClientDebugLevel())
- if client.lg != nil {
- client.lg = client.lg.Named("etcd-client")
+ lg, err = logutil.CreateDefaultZapLogger(etcdClientDebugLevel())
+ if lg != nil {
+ lg = lg.Named("etcd-client")
}
}
if err != nil {
return nil, err
}
+ client.lg.Store(lg)
if cfg.Username != "" && cfg.Password != "" {
client.Username = cfg.Username
client.Password = cfg.Password
client.authTokenBundle = credentials.NewPerRPCCredentialBundle()
}
+
+ if cfg.Token != "" {
+ client.Token = cfg.Token
+ client.authTokenBundle = credentials.NewPerRPCCredentialBundle()
+ }
+
if cfg.MaxCallSendMsgSize > 0 || cfg.MaxCallRecvMsgSize > 0 {
if cfg.MaxCallRecvMsgSize > 0 && cfg.MaxCallSendMsgSize > cfg.MaxCallRecvMsgSize {
return nil, fmt.Errorf("gRPC message recv limit (%d bytes) must be greater than send limit (%d bytes)", cfg.MaxCallRecvMsgSize, cfg.MaxCallSendMsgSize)
@@ -491,10 +494,10 @@ func (c *Client) roundRobinQuorumBackoff(waitBetween time.Duration, jitterFracti
n := uint(len(c.Endpoints()))
quorum := (n/2 + 1)
if attempt%quorum == 0 {
- c.lg.Debug("backoff", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum), zap.Duration("waitBetween", waitBetween), zap.Float64("jitterFraction", jitterFraction))
+ c.GetLogger().Debug("backoff", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum), zap.Duration("waitBetween", waitBetween), zap.Float64("jitterFraction", jitterFraction))
return jitterUp(waitBetween, jitterFraction)
}
- c.lg.Debug("backoff skipped", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum))
+ c.GetLogger().Debug("backoff skipped", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum))
return 0
}
}
diff --git a/client/v3/client_test.go b/client/v3/client_test.go
index cbe94a411b36..d193aa9c748e 100644
--- a/client/v3/client_test.go
+++ b/client/v3/client_test.go
@@ -29,6 +29,8 @@ import (
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"google.golang.org/grpc"
+ "google.golang.org/grpc/health"
+ healthpb "google.golang.org/grpc/health/grpc_health_v1"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
@@ -47,12 +49,25 @@ func NewClient(t *testing.T, cfg Config) (*Client, error) {
func TestDialCancel(t *testing.T) {
testutil.RegisterLeakDetection(t)
- // accept first connection so client is created with dial timeout
- ln, err := net.Listen("unix", "dialcancel:12345")
+ // Start a real gRPC endpoint with health service so initial dial readiness
+ // check succeeds before switching endpoints below.
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
defer ln.Close()
- ep := "unix://dialcancel:12345"
+ srv := grpc.NewServer()
+ healthpb.RegisterHealthServer(srv, health.NewServer())
+ serveDone := make(chan error)
+ go func() {
+ defer close(serveDone)
+ srv.Serve(ln)
+ }()
+ defer func() {
+ srv.Stop()
+ <-serveDone
+ }()
+
+ ep := ln.Addr().String()
cfg := Config{
Endpoints: []string{ep},
DialTimeout: 30 * time.Second,
@@ -93,65 +108,6 @@ func TestDialCancel(t *testing.T) {
}
}
-func TestDialTimeout(t *testing.T) {
- testutil.RegisterLeakDetection(t)
-
- wantError := context.DeadlineExceeded
-
- // grpc.WithBlock to block until connection up or timeout
- testCfgs := []Config{
- {
- Endpoints: []string{"http://254.0.0.1:12345"},
- DialTimeout: 2 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
- },
- {
- Endpoints: []string{"http://254.0.0.1:12345"},
- DialTimeout: time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
- Username: "abc",
- Password: "def",
- },
- }
-
- for i, cfg := range testCfgs {
- donec := make(chan error, 1)
- go func(cfg Config, i int) {
- // without timeout, dial continues forever on ipv4 black hole
- c, err := NewClient(t, cfg)
- if c != nil || err == nil {
- t.Errorf("#%d: new client should fail", i)
- }
- donec <- err
- }(cfg, i)
-
- time.Sleep(10 * time.Millisecond)
-
- select {
- case err := <-donec:
- t.Errorf("#%d: dial didn't wait (%v)", i, err)
- default:
- }
-
- select {
- case <-time.After(5 * time.Second):
- t.Errorf("#%d: failed to timeout dial on time", i)
- case err := <-donec:
- if err.Error() != wantError.Error() {
- t.Errorf("#%d: unexpected error '%v', want '%v'", i, err, wantError)
- }
- }
- }
-}
-
-func TestDialNoTimeout(t *testing.T) {
- cfg := Config{Endpoints: []string{"127.0.0.1:12345"}}
- c, err := NewClient(t, cfg)
- require.NotNilf(t, c, "new client with DialNoWait should succeed, got %v", err)
- require.NoErrorf(t, err, "new client with DialNoWait should succeed")
- c.Close()
-}
-
func TestMaxUnaryRetries(t *testing.T) {
maxUnaryRetries := uint(10)
cfg := Config{
@@ -196,18 +152,18 @@ func TestBackoffJitterFraction(t *testing.T) {
func TestIsHaltErr(t *testing.T) {
assert.Truef(t,
- isHaltErr(context.TODO(), errors.New("etcdserver: some etcdserver error")),
+ isHaltErr(t.Context(), errors.New("etcdserver: some etcdserver error")),
"error created by errors.New should be unavailable error",
)
assert.Falsef(t,
- isHaltErr(context.TODO(), rpctypes.ErrGRPCStopped),
+ isHaltErr(t.Context(), rpctypes.ErrGRPCStopped),
`error "%v" should not be halt error`, rpctypes.ErrGRPCStopped,
)
assert.Falsef(t,
- isHaltErr(context.TODO(), rpctypes.ErrGRPCNoLeader),
+ isHaltErr(t.Context(), rpctypes.ErrGRPCNoLeader),
`error "%v" should not be halt error`, rpctypes.ErrGRPCNoLeader,
)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
assert.Falsef(t,
isHaltErr(ctx, nil),
"no error and active context should be halt error",
@@ -221,18 +177,18 @@ func TestIsHaltErr(t *testing.T) {
func TestIsUnavailableErr(t *testing.T) {
assert.Falsef(t,
- isUnavailableErr(context.TODO(), errors.New("etcdserver: some etcdserver error")),
+ isUnavailableErr(t.Context(), errors.New("etcdserver: some etcdserver error")),
"error created by errors.New should not be unavailable error",
)
assert.Truef(t,
- isUnavailableErr(context.TODO(), rpctypes.ErrGRPCStopped),
+ isUnavailableErr(t.Context(), rpctypes.ErrGRPCStopped),
`error "%v" should be unavailable error`, rpctypes.ErrGRPCStopped,
)
assert.Falsef(t,
- isUnavailableErr(context.TODO(), rpctypes.ErrGRPCNotCapable),
+ isUnavailableErr(t.Context(), rpctypes.ErrGRPCNotCapable),
"error %v should not be unavailable error", rpctypes.ErrGRPCNotCapable,
)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
assert.Falsef(t,
isUnavailableErr(ctx, nil),
"no error and active context should not be unavailable error",
@@ -245,7 +201,7 @@ func TestIsUnavailableErr(t *testing.T) {
}
func TestCloseCtxClient(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
c := NewCtxClient(ctx)
err := c.Close()
// Close returns ctx.toErr, a nil error means an open Done channel
@@ -255,24 +211,24 @@ func TestCloseCtxClient(t *testing.T) {
}
func TestWithLogger(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
c := NewCtxClient(ctx)
- if c.lg == nil {
+ if c.lg.Load() == nil {
t.Errorf("unexpected nil in *zap.Logger")
}
c.WithLogger(nil)
- if c.lg != nil {
+ if c.GetLogger() != nil {
t.Errorf("WithLogger should modify *zap.Logger")
}
}
func TestZapWithLogger(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
lg := zap.NewNop()
c := NewCtxClient(ctx, WithZapLogger(lg))
- if c.lg != lg {
+ if c.GetLogger() != lg {
t.Errorf("WithZapLogger should modify *zap.Logger")
}
}
@@ -317,6 +273,75 @@ func TestAuthTokenBundleNoOverwrite(t *testing.T) {
}
}
+func TestNewWithOnlyJWT(t *testing.T) {
+ // This call in particular changes working directory to the tmp dir of
+ // the test. The `etcd-auth-test:1` can be created in local directory,
+ // not exceeding the longest allowed path on OsX.
+ testutil.BeforeTest(t)
+
+ // Create a mock AuthServer to handle Authenticate RPCs.
+ lis, err := net.Listen("unix", "etcd-auth-test:1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer lis.Close()
+ addr := "unix://" + lis.Addr().String()
+ srv := grpc.NewServer()
+ // Having a token removes the need to ever call Authenticate on the
+ // server. If that happens then this will cause a connection failure.
+ etcdserverpb.RegisterAuthServer(srv, mockFailingAuthServer{})
+ go srv.Serve(lis)
+ defer srv.Stop()
+
+ c, err := NewClient(t, Config{
+ DialTimeout: 5 * time.Second,
+ Endpoints: []string{addr},
+ Token: "foo",
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+
+ meta, err := c.authTokenBundle.PerRPCCredentials().GetRequestMetadata(t.Context(), "")
+ if err != nil {
+ t.Errorf("Error building request metadata: %s", err)
+ }
+
+ if tok, ok := meta[rpctypes.TokenFieldNameGRPC]; !ok {
+ t.Error("Token was not successfully set in the auth bundle")
+ } else if tok != "foo" {
+ t.Errorf("Incorrect token set in auth bundle, got '%s', expected 'foo'", tok)
+ }
+}
+
+func TestNewOnlyJWTExclusivity(t *testing.T) {
+ testutil.BeforeTest(t)
+
+ // Create a mock AuthServer to handle Authenticate RPCs.
+ lis, err := net.Listen("unix", "etcd-auth-test:1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer lis.Close()
+ addr := "unix://" + lis.Addr().String()
+ srv := grpc.NewServer()
+ // Having a token removes the need to ever call Authenticate on the
+ // server. If that happens then this will cause a connection failure.
+ etcdserverpb.RegisterAuthServer(srv, mockFailingAuthServer{})
+ go srv.Serve(lis)
+ defer srv.Stop()
+
+ _, err = NewClient(t, Config{
+ DialTimeout: 5 * time.Second,
+ Endpoints: []string{addr},
+ Token: "foo",
+ Username: "user",
+ Password: "pass",
+ })
+ require.ErrorIs(t, ErrMutuallyExclusiveCfg, err)
+}
+
func TestSyncFiltersMembers(t *testing.T) {
c, _ := NewClient(t, Config{Endpoints: []string{"http://254.0.0.1:12345"}})
defer c.Close()
@@ -327,7 +352,7 @@ func TestSyncFiltersMembers(t *testing.T) {
{ID: 2, Name: "isStartedAndNotLearner", ClientURLs: []string{"http://254.0.0.3:12345"}, IsLearner: false},
},
}
- c.Sync(context.Background())
+ c.Sync(t.Context())
endpoints := c.Endpoints()
if len(endpoints) != 1 || endpoints[0] != "http://254.0.0.3:12345" {
@@ -421,7 +446,7 @@ func TestClientRejectOldCluster(t *testing.T) {
endpointToVersion[tt.endpoints[j]] = tt.versions[j]
}
c := &Client{
- ctx: context.Background(),
+ ctx: t.Context(),
endpoints: tt.endpoints,
epMu: new(sync.RWMutex),
Maintenance: &mockMaintenance{
@@ -476,8 +501,16 @@ func (mm mockMaintenance) Downgrade(ctx context.Context, action DowngradeAction,
return nil, nil
}
+type mockFailingAuthServer struct {
+ etcdserverpb.UnimplementedAuthServer
+}
+
+func (mockFailingAuthServer) Authenticate(context.Context, *etcdserverpb.AuthenticateRequest) (*etcdserverpb.AuthenticateResponse, error) {
+ return nil, errors.New("this auth server always fails")
+}
+
type mockAuthServer struct {
- *etcdserverpb.UnimplementedAuthServer
+ etcdserverpb.UnimplementedAuthServer
}
func (mockAuthServer) Authenticate(context.Context, *etcdserverpb.AuthenticateRequest) (*etcdserverpb.AuthenticateResponse, error) {
diff --git a/client/v3/compare.go b/client/v3/compare.go
index 663fdb4d2064..25a6e6557658 100644
--- a/client/v3/compare.go
+++ b/client/v3/compare.go
@@ -15,6 +15,8 @@
package clientv3
import (
+ "google.golang.org/protobuf/proto"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
@@ -30,7 +32,25 @@ const (
CompareValue
)
-type Cmp pb.Compare
+type Cmp struct {
+ c *pb.Compare
+}
+
+func FromCompare(c *pb.Compare) Cmp {
+ return Cmp{c: cloneCompare(c)}
+}
+
+func (cmp *Cmp) GetCompare() *pb.Compare {
+ cmp.ensureCompare()
+ return cmp.c
+}
+
+func (cmp *Cmp) Clone() Cmp {
+ if cmp.c == nil {
+ return Cmp{}
+ }
+ return Cmp{c: cloneCompare(cmp.c)}
+}
func Compare(cmp Cmp, result string, v any) Cmp {
var r pb.Compare_CompareResult
@@ -48,22 +68,24 @@ func Compare(cmp Cmp, result string, v any) Cmp {
panic("Unknown result op")
}
- cmp.Result = r
- switch cmp.Target {
+ cmp = cmp.Clone()
+ cmp.ensureCompare()
+ cmp.c.Result = r
+ switch cmp.c.Target {
case pb.Compare_VALUE:
val, ok := v.(string)
if !ok {
panic("bad compare value")
}
- cmp.TargetUnion = &pb.Compare_Value{Value: []byte(val)}
+ cmp.c.TargetUnion = &pb.Compare_Value{Value: []byte(val)}
case pb.Compare_VERSION:
- cmp.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}
+ cmp.c.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}
case pb.Compare_CREATE:
- cmp.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
+ cmp.c.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
case pb.Compare_MOD:
- cmp.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}
+ cmp.c.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}
case pb.Compare_LEASE:
- cmp.TargetUnion = &pb.Compare_Lease{Lease: mustInt64orLeaseID(v)}
+ cmp.c.TargetUnion = &pb.Compare_Lease{Lease: mustInt64orLeaseID(v)}
default:
panic("Unknown compare type")
}
@@ -71,53 +93,74 @@ func Compare(cmp Cmp, result string, v any) Cmp {
}
func Value(key string) Cmp {
- return Cmp{Key: []byte(key), Target: pb.Compare_VALUE}
+ return Cmp{c: &pb.Compare{Key: []byte(key), Target: pb.Compare_VALUE}}
}
func Version(key string) Cmp {
- return Cmp{Key: []byte(key), Target: pb.Compare_VERSION}
+ return Cmp{c: &pb.Compare{Key: []byte(key), Target: pb.Compare_VERSION}}
}
func CreateRevision(key string) Cmp {
- return Cmp{Key: []byte(key), Target: pb.Compare_CREATE}
+ return Cmp{c: &pb.Compare{Key: []byte(key), Target: pb.Compare_CREATE}}
}
func ModRevision(key string) Cmp {
- return Cmp{Key: []byte(key), Target: pb.Compare_MOD}
+ return Cmp{c: &pb.Compare{Key: []byte(key), Target: pb.Compare_MOD}}
}
// LeaseValue compares a key's LeaseID to a value of your choosing. The empty
// LeaseID is 0, otherwise known as `NoLease`.
func LeaseValue(key string) Cmp {
- return Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
+ return Cmp{c: &pb.Compare{Key: []byte(key), Target: pb.Compare_LEASE}}
+}
+
+func (cmp *Cmp) ensureCompare() {
+ if cmp.c == nil {
+ cmp.c = &pb.Compare{}
+ }
}
// KeyBytes returns the byte slice holding with the comparison key.
-func (cmp *Cmp) KeyBytes() []byte { return cmp.Key }
+func (cmp *Cmp) KeyBytes() []byte {
+ if cmp == nil {
+ return nil
+ }
+ return cmp.c.GetKey()
+}
// WithKeyBytes sets the byte slice for the comparison key.
-func (cmp *Cmp) WithKeyBytes(key []byte) { cmp.Key = key }
+func (cmp *Cmp) WithKeyBytes(key []byte) {
+ cmp.ensureCompare()
+ cmp.c.Key = key
+}
// ValueBytes returns the byte slice holding the comparison value, if any.
func (cmp *Cmp) ValueBytes() []byte {
- if tu, ok := cmp.TargetUnion.(*pb.Compare_Value); ok {
+ if tu, ok := cmp.GetCompare().GetTargetUnion().(*pb.Compare_Value); ok {
return tu.Value
}
return nil
}
// WithValueBytes sets the byte slice for the comparison's value.
-func (cmp *Cmp) WithValueBytes(v []byte) { cmp.TargetUnion.(*pb.Compare_Value).Value = v }
+func (cmp *Cmp) WithValueBytes(v []byte) {
+ cmp.ensureCompare()
+ cmp.c.TargetUnion.(*pb.Compare_Value).Value = v
+}
// WithRange sets the comparison to scan the range [key, end).
func (cmp Cmp) WithRange(end string) Cmp {
- cmp.RangeEnd = []byte(end)
+ cmp = cmp.Clone()
+ cmp.ensureCompare()
+ cmp.c.RangeEnd = []byte(end)
return cmp
}
// WithPrefix sets the comparison to scan all keys prefixed by the key.
func (cmp Cmp) WithPrefix() Cmp {
- cmp.RangeEnd = getPrefix(cmp.Key)
+ cmp = cmp.Clone()
+ cmp.ensureCompare()
+ cmp.c.RangeEnd = getPrefix(cmp.c.GetKey())
return cmp
}
@@ -140,3 +183,10 @@ func mustInt64orLeaseID(val any) int64 {
}
return mustInt64(val)
}
+
+func cloneCompare(c *pb.Compare) *pb.Compare {
+ if c == nil {
+ return nil
+ }
+ return proto.Clone(c).(*pb.Compare)
+}
diff --git a/client/v3/compare_bench_test.go b/client/v3/compare_bench_test.go
new file mode 100644
index 000000000000..970dc9b24755
--- /dev/null
+++ b/client/v3/compare_bench_test.go
@@ -0,0 +1,80 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 clientv3
+
+import (
+ "context"
+ "testing"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+)
+
+var (
+ benchmarkTxnRequestSink *pb.TxnRequest
+ benchmarkTxnSink *txn
+)
+
+func BenchmarkTxnIfSingleCmp(b *testing.B) {
+ const key = "/registry/pods/default/pod-0"
+ const expectedRevision int64 = 123
+
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ txn := (&txn{ctx: context.Background()}).If(
+ Compare(ModRevision(key), "=", expectedRevision),
+ ).(*txn)
+ benchmarkTxnSink = txn
+ }
+}
+
+func BenchmarkKubernetesOptimisticPutTxnBuild(b *testing.B) {
+ const (
+ key = "/registry/pods/default/pod-0"
+ value = "value"
+ expectedRevision = int64(123)
+ leaseID = LeaseID(456)
+ )
+
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ txn := (&txn{ctx: context.Background()}).If(
+ Compare(ModRevision(key), "=", expectedRevision),
+ ).Then(
+ OpPut(key, value, WithLease(leaseID)),
+ ).Else(
+ OpGet(key),
+ ).(*txn)
+ benchmarkTxnRequestSink = &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
+ }
+}
+
+func BenchmarkOpTxnSingleCmpToTxnRequest(b *testing.B) {
+ const (
+ key = "/registry/pods/default/pod-0"
+ value = "value"
+ expectedRevision = int64(123)
+ leaseID = LeaseID(456)
+ )
+
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ op := OpTxn(
+ []Cmp{Compare(ModRevision(key), "=", expectedRevision)},
+ []Op{OpPut(key, value, WithLease(leaseID))},
+ []Op{OpGet(key)},
+ )
+ benchmarkTxnRequestSink = op.toTxnRequest()
+ }
+}
diff --git a/client/v3/compare_test.go b/client/v3/compare_test.go
new file mode 100644
index 000000000000..11cd05f45367
--- /dev/null
+++ b/client/v3/compare_test.go
@@ -0,0 +1,78 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 clientv3
+
+import (
+ "testing"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+)
+
+func TestCompareClonesInput(t *testing.T) {
+ base := ModRevision("foo")
+ cmp := Compare(base, "=", 7)
+
+ base.WithKeyBytes([]byte("bar"))
+
+ if got := string(cmp.KeyBytes()); got != "foo" {
+ t.Fatalf("cmp key = %q, want %q", got, "foo")
+ }
+ if got := cmp.GetCompare().GetModRevision(); got != 7 {
+ t.Fatalf("cmp mod revision = %d, want %d", got, 7)
+ }
+}
+
+func TestWithRangeClonesInput(t *testing.T) {
+ base := ModRevision("foo")
+ ranged := base.WithRange("zoo")
+
+ if got := string(base.GetCompare().GetRangeEnd()); got != "" {
+ t.Fatalf("base range end = %q, want empty", got)
+ }
+ if got := string(ranged.GetCompare().GetRangeEnd()); got != "zoo" {
+ t.Fatalf("ranged range end = %q, want %q", got, "zoo")
+ }
+}
+
+func TestTxnIfClonesCmp(t *testing.T) {
+ cmp := Compare(ModRevision("foo"), "=", 7)
+ txn := (&txn{}).If(cmp).(*txn)
+
+ cmp.WithKeyBytes([]byte("bar"))
+ cmp.GetCompare().Result = pb.Compare_NOT_EQUAL
+
+ if got := string(txn.cmps[0].GetKey()); got != "foo" {
+ t.Fatalf("txn compare key = %q, want %q", got, "foo")
+ }
+ if got := txn.cmps[0].GetResult(); got != pb.Compare_EQUAL {
+ t.Fatalf("txn compare result = %v, want %v", got, pb.Compare_EQUAL)
+ }
+}
+
+func TestOpTxnClonesCmps(t *testing.T) {
+ cmp := Compare(ModRevision("foo"), "=", 7)
+ op := OpTxn([]Cmp{cmp}, nil, nil)
+
+ cmp.WithKeyBytes([]byte("bar"))
+ cmp.GetCompare().Result = pb.Compare_NOT_EQUAL
+
+ req := op.toTxnRequest()
+ if got := string(req.Compare[0].GetKey()); got != "foo" {
+ t.Fatalf("txn request compare key = %q, want %q", got, "foo")
+ }
+ if got := req.Compare[0].GetResult(); got != pb.Compare_EQUAL {
+ t.Fatalf("txn request compare result = %v, want %v", got, pb.Compare_EQUAL)
+ }
+}
diff --git a/client/v3/concurrency/election.go b/client/v3/concurrency/election.go
index ac1303dd8b48..7a9e2754faf7 100644
--- a/client/v3/concurrency/election.go
+++ b/client/v3/concurrency/election.go
@@ -158,19 +158,19 @@ func (e *Election) Leader(ctx context.Context) (*v3.GetResponse, error) {
}
// Observe returns a channel that reliably observes ordered leader proposals
-// as GetResponse values on every current elected leader key. It will not
+// as GetResponse pointers on every current elected leader key. It will not
// necessarily fetch all historical leader updates, but will always post the
// most recent leader value.
//
// The channel closes when the context is canceled or the underlying watcher
// is otherwise disrupted.
-func (e *Election) Observe(ctx context.Context) <-chan v3.GetResponse {
- retc := make(chan v3.GetResponse)
+func (e *Election) Observe(ctx context.Context) <-chan *v3.GetResponse {
+ retc := make(chan *v3.GetResponse)
go e.observe(ctx, retc)
return retc
}
-func (e *Election) observe(ctx context.Context, ch chan<- v3.GetResponse) {
+func (e *Election) observe(ctx context.Context, ch chan<- *v3.GetResponse) {
client := e.session.Client()
defer close(ch)
@@ -196,8 +196,8 @@ func (e *Election) observe(ctx context.Context, ch chan<- v3.GetResponse) {
}
// only accept puts; a delete will make observe() spin
for _, ev := range wr.Events {
- if ev.Type == mvccpb.PUT {
- hdr, kv = &wr.Header, ev.Kv
+ if ev.Type == mvccpb.Event_PUT {
+ hdr, kv = wr.Header, ev.Kv
// may have multiple revs; hdr.rev = the last rev
// set to kv's rev in case batch has multiple Puts
hdr.Revision = kv.ModRevision
@@ -211,7 +211,7 @@ func (e *Election) observe(ctx context.Context, ch chan<- v3.GetResponse) {
}
select {
- case ch <- v3.GetResponse{Header: hdr, Kvs: []*mvccpb.KeyValue{kv}}:
+ case ch <- &v3.GetResponse{Header: hdr, Kvs: []*mvccpb.KeyValue{kv}}:
case <-ctx.Done():
return
}
@@ -226,14 +226,14 @@ func (e *Election) observe(ctx context.Context, ch chan<- v3.GetResponse) {
return
}
for _, ev := range wr.Events {
- if ev.Type == mvccpb.DELETE {
+ if ev.Type == mvccpb.Event_DELETE {
keyDeleted = true
break
}
- resp.Header = &wr.Header
- resp.Kvs = []*mvccpb.KeyValue{ev.Kv}
select {
- case ch <- *resp:
+ case ch <- &v3.GetResponse{
+ Header: wr.Header,
+ Kvs: []*mvccpb.KeyValue{ev.Kv}}:
case <-cctx.Done():
cancel()
return
diff --git a/client/v3/concurrency/key.go b/client/v3/concurrency/key.go
index 92e365c47151..fe3279893353 100644
--- a/client/v3/concurrency/key.go
+++ b/client/v3/concurrency/key.go
@@ -30,7 +30,7 @@ func waitDelete(ctx context.Context, client *v3.Client, key string, rev int64) e
wch := client.Watch(cctx, key, v3.WithRev(rev))
for wr = range wch {
for _, ev := range wr.Events {
- if ev.Type == mvccpb.DELETE {
+ if ev.Type == mvccpb.Event_DELETE {
return nil
}
}
diff --git a/client/v3/concurrency/stm.go b/client/v3/concurrency/stm.go
index 49a82181db70..696baf869ca1 100644
--- a/client/v3/concurrency/stm.go
+++ b/client/v3/concurrency/stm.go
@@ -47,7 +47,7 @@ const (
// for write conflicts.
SerializableSnapshot Isolation = iota
// Serializable reads within the same transaction attempt return data
- // from the at the revision of the first read.
+ // from the revision of the first read.
Serializable
// RepeatableReads reads within the same transaction attempt always
// return the same data.
@@ -65,15 +65,16 @@ type stmOptions struct {
prefetch []string
}
-type stmOption func(*stmOptions)
+// STMOption configures STM.
+type STMOption func(*stmOptions)
// WithIsolation specifies the transaction isolation level.
-func WithIsolation(lvl Isolation) stmOption {
+func WithIsolation(lvl Isolation) STMOption {
return func(so *stmOptions) { so.iso = lvl }
}
// WithAbortContext specifies the context for permanently aborting the transaction.
-func WithAbortContext(ctx context.Context) stmOption {
+func WithAbortContext(ctx context.Context) STMOption {
return func(so *stmOptions) { so.ctx = ctx }
}
@@ -81,12 +82,12 @@ func WithAbortContext(ctx context.Context) stmOption {
// If an STM transaction will unconditionally fetch a set of keys, prefetching
// those keys will save the round-trip cost from requesting each key one by one
// with Get().
-func WithPrefetch(keys ...string) stmOption {
+func WithPrefetch(keys ...string) STMOption {
return func(so *stmOptions) { so.prefetch = append(so.prefetch, keys...) }
}
// NewSTM initiates a new STM instance, using serializable snapshot isolation by default.
-func NewSTM(c *v3.Client, apply func(STM) error, so ...stmOption) (*v3.TxnResponse, error) {
+func NewSTM(c *v3.Client, apply func(STM) error, so ...STMOption) (*v3.TxnResponse, error) {
opts := &stmOptions{ctx: c.Ctx()}
for _, f := range so {
f(opts)
diff --git a/client/v3/config.go b/client/v3/config.go
index 8351828d2f90..153bb97bbc41 100644
--- a/client/v3/config.go
+++ b/client/v3/config.go
@@ -33,7 +33,14 @@ type Config struct {
// 0 disables auto-sync. By default auto-sync is disabled.
AutoSyncInterval time.Duration `json:"auto-sync-interval"`
- // DialTimeout is the timeout for failing to establish a connection.
+ // DialTimeout is the timeout used for certain operations, such as fetching
+ // an authentication token and checking the cluster version (when
+ // RejectOldCluster is true). It is also used to derive the initial
+ // keep-alive timeout for lease keep-alives (DialTimeout + 1s).
+ // It is NOT used to bound the gRPC connection establishment itself,
+ // because client creation is non-blocking since
+ // https://github.com/etcd-io/etcd/pull/21832.
+ // A value of 0 means no timeout is applied to those operations.
DialTimeout time.Duration `json:"dial-timeout"`
// DialKeepAliveTime is the time after which client pings the server to see if
@@ -66,12 +73,15 @@ type Config struct {
// Password is a password for authentication.
Password string `json:"password"`
+ // Token is a JWT used for authentication instead of a password.
+ Token string `json:"token"`
+
// RejectOldCluster when set will refuse to create a client against an outdated cluster.
RejectOldCluster bool `json:"reject-old-cluster"`
// DialOptions is a list of dial options for the grpc client (e.g., for interceptors).
- // For example, pass "grpc.WithBlock()" to block until the underlying connection is up.
- // Without this, Dial returns immediately and connecting the server happens in background.
+ // Note that grpc.NewClient ignores options that are specific to grpc.Dial such as
+ // "grpc.WithBlock()".
DialOptions []grpc.DialOption
// Context is the default client context; it can be used to cancel grpc dial out and
@@ -130,6 +140,7 @@ type SecureConfig struct {
type AuthConfig struct {
Username string `json:"username"`
Password string `json:"password"`
+ Token string `json:"token"`
}
func (cs *ConfigSpec) Clone() *ConfigSpec {
@@ -157,7 +168,7 @@ func (cs *ConfigSpec) Clone() *ConfigSpec {
}
func (cfg AuthConfig) Empty() bool {
- return cfg.Username == "" && cfg.Password == ""
+ return cfg.Username == "" && cfg.Password == "" && cfg.Token == ""
}
// NewClientConfig creates a Config based on the provided ConfigSpec.
@@ -180,6 +191,7 @@ func NewClientConfig(confSpec *ConfigSpec, lg *zap.Logger) (*Config, error) {
if confSpec.Auth != nil {
cfg.Username = confSpec.Auth.Username
cfg.Password = confSpec.Auth.Password
+ cfg.Token = confSpec.Auth.Token
}
return cfg, nil
diff --git a/client/v3/config_test.go b/client/v3/config_test.go
index 1fe2fb2d391e..eb3c8bcaacd0 100644
--- a/client/v3/config_test.go
+++ b/client/v3/config_test.go
@@ -71,6 +71,25 @@ func TestNewClientConfig(t *testing.T) {
Password: "changeme",
},
},
+ {
+ name: "JWT specified",
+ spec: ConfigSpec{
+ Endpoints: []string{"http://192.168.0.12:2379"},
+ DialTimeout: 1 * time.Second,
+ KeepAliveTime: 4 * time.Second,
+ KeepAliveTimeout: 6 * time.Second,
+ Auth: &AuthConfig{
+ Token: "test",
+ },
+ },
+ expectedConf: Config{
+ Endpoints: []string{"http://192.168.0.12:2379"},
+ DialTimeout: 1 * time.Second,
+ DialKeepAliveTime: 4 * time.Second,
+ DialKeepAliveTimeout: 6 * time.Second,
+ Token: "test",
+ },
+ },
{
name: "default secure transport",
spec: ConfigSpec{
diff --git a/client/v3/credentials/credentials_test.go b/client/v3/credentials/credentials_test.go
index 0db241e3c413..ca2bd0cbf1d1 100644
--- a/client/v3/credentials/credentials_test.go
+++ b/client/v3/credentials/credentials_test.go
@@ -15,7 +15,6 @@
package credentials
import (
- "context"
"testing"
"github.com/stretchr/testify/assert"
@@ -25,7 +24,7 @@ import (
func TestUpdateAuthToken(t *testing.T) {
bundle := NewPerRPCCredentialBundle()
- ctx := context.TODO()
+ ctx := t.Context()
metadataBeforeUpdate, _ := bundle.PerRPCCredentials().GetRequestMetadata(ctx)
assert.Empty(t, metadataBeforeUpdate)
diff --git a/client/v3/ctx_test.go b/client/v3/ctx_test.go
index 2df734e4ea2d..ea0870236481 100644
--- a/client/v3/ctx_test.go
+++ b/client/v3/ctx_test.go
@@ -15,7 +15,6 @@
package clientv3
import (
- "context"
"reflect"
"testing"
@@ -27,7 +26,7 @@ import (
)
func TestMetadataWithRequireLeader(t *testing.T) {
- ctx := context.TODO()
+ ctx := t.Context()
_, ok := metadata.FromOutgoingContext(ctx)
require.Falsef(t, ok, "expected no outgoing metadata ctx key")
@@ -48,7 +47,7 @@ func TestMetadataWithRequireLeader(t *testing.T) {
}
func TestMetadataWithClientAPIVersion(t *testing.T) {
- ctx := withVersion(WithRequireLeader(context.TODO()))
+ ctx := withVersion(WithRequireLeader(t.Context()))
md, ok := metadata.FromOutgoingContext(ctx)
require.Truef(t, ok, "expected outgoing metadata ctx key")
diff --git a/client/v3/experimental/recipes/barrier.go b/client/v3/experimental/recipes/barrier.go
index daf4abefb3a3..6f9093ab75a5 100644
--- a/client/v3/experimental/recipes/barrier.go
+++ b/client/v3/experimental/recipes/barrier.go
@@ -61,6 +61,6 @@ func (b *Barrier) Wait() error {
b.client,
b.key,
resp.Header.Revision+1,
- []mvccpb.Event_EventType{mvccpb.DELETE})
+ []mvccpb.Event_EventType{mvccpb.Event_DELETE})
return err
}
diff --git a/client/v3/experimental/recipes/double_barrier.go b/client/v3/experimental/recipes/double_barrier.go
index e0d00247f1c7..b0af2aa6e8ca 100644
--- a/client/v3/experimental/recipes/double_barrier.go
+++ b/client/v3/experimental/recipes/double_barrier.go
@@ -92,7 +92,7 @@ func (b *DoubleBarrier) Enter() error {
client,
b.key+"/ready",
ek.Revision(),
- []mvccpb.Event_EventType{mvccpb.PUT})
+ []mvccpb.Event_EventType{mvccpb.Event_PUT})
return err
}
@@ -147,7 +147,7 @@ func (b *DoubleBarrier) Leave() error {
client,
string(highest.Key),
highest.ModRevision,
- []mvccpb.Event_EventType{mvccpb.DELETE})
+ []mvccpb.Event_EventType{mvccpb.Event_DELETE})
if err != nil {
return err
}
@@ -164,7 +164,7 @@ func (b *DoubleBarrier) Leave() error {
client,
key,
lowest.ModRevision,
- []mvccpb.Event_EventType{mvccpb.DELETE})
+ []mvccpb.Event_EventType{mvccpb.Event_DELETE})
if err != nil {
return err
}
diff --git a/client/v3/experimental/recipes/priority_queue.go b/client/v3/experimental/recipes/priority_queue.go
index 1b26067466fa..84a99f208b7e 100644
--- a/client/v3/experimental/recipes/priority_queue.go
+++ b/client/v3/experimental/recipes/priority_queue.go
@@ -65,7 +65,7 @@ func (q *PriorityQueue) Dequeue() (string, error) {
q.client,
q.key,
resp.Header.Revision,
- []mvccpb.Event_EventType{mvccpb.PUT})
+ []mvccpb.Event_EventType{mvccpb.Event_PUT})
if err != nil {
return "", err
}
diff --git a/client/v3/experimental/recipes/queue.go b/client/v3/experimental/recipes/queue.go
index 9c6b0378e110..025723720c8b 100644
--- a/client/v3/experimental/recipes/queue.go
+++ b/client/v3/experimental/recipes/queue.go
@@ -62,7 +62,7 @@ func (q *Queue) Dequeue() (string, error) {
q.client,
q.keyPrefix,
resp.Header.Revision,
- []mvccpb.Event_EventType{mvccpb.PUT})
+ []mvccpb.Event_EventType{mvccpb.Event_PUT})
if err != nil {
return "", err
}
diff --git a/client/v3/experimental/recipes/rwmutex.go b/client/v3/experimental/recipes/rwmutex.go
index d961ead3f04f..34a8f5363b27 100644
--- a/client/v3/experimental/recipes/rwmutex.go
+++ b/client/v3/experimental/recipes/rwmutex.go
@@ -81,7 +81,7 @@ func (rwm *RWMutex) waitOnLastRev(pfx string) (bool, error) {
client,
string(lastKey.Kvs[0].Key),
rwm.myKey.Revision(),
- []mvccpb.Event_EventType{mvccpb.DELETE})
+ []mvccpb.Event_EventType{mvccpb.Event_DELETE})
return false, err
}
diff --git a/client/v3/go.mod b/client/v3/go.mod
index 62894c140a13..4695496ee1c8 100644
--- a/client/v3/go.mod
+++ b/client/v3/go.mod
@@ -1,44 +1,43 @@
module go.etcd.io/etcd/client/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/coreos/go-semver v0.3.1
github.com/dustin/go-humanize v1.0.1
- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
- github.com/prometheus/client_golang v1.20.5
+ github.com/golang/protobuf v1.5.4
+ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
+ github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.uber.org/zap v1.27.0
- google.golang.org/grpc v1.79.3
- sigs.k8s.io/yaml v1.4.0
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.uber.org/zap v1.27.1
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
+ sigs.k8s.io/yaml v1.6.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang/protobuf v1.5.4 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.62.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/protobuf v1.36.10 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -46,12 +45,3 @@ replace (
go.etcd.io/etcd/api/v3 => ../../api
go.etcd.io/etcd/client/pkg/v3 => ../pkg
)
-
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-replace (
- go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/pkg/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/tests/v3 => ./FORBIDDEN_DEPENDENCY
-)
diff --git a/client/v3/go.sum b/client/v3/go.sum
index 536fe351bef7..decb2b8b97e0 100644
--- a/client/v3/go.sum
+++ b/client/v3/go.sum
@@ -4,36 +4,30 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -42,85 +36,62 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/client/v3/kv.go b/client/v3/kv.go
index 8d0c595d1e43..c0d3ace313a4 100644
--- a/client/v3/kv.go
+++ b/client/v3/kv.go
@@ -16,7 +16,10 @@ package clientv3
import (
"context"
+ "errors"
+ "io"
+ "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
"google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
@@ -27,6 +30,7 @@ type (
CompactResponse pb.CompactionResponse
PutResponse pb.PutResponse
GetResponse pb.RangeResponse
+ GetStreamChan <-chan RangeStreamResponse
DeleteResponse pb.DeleteRangeResponse
TxnResponse pb.TxnResponse
)
@@ -48,6 +52,15 @@ type KV interface {
// When passed WithSort(), the keys will be sorted.
Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)
+ // RangeStream retrieves keys.
+ // By default, will return the value for "key", if any.
+ // When passed WithRange(end), will return the keys in the range [key, end).
+ // When passed WithFromKey(), returns keys greater than or equal to key.
+ // When passed WithRev(rev) with rev > 0, retrieves keys at the given revision;
+ // if the required revision is compacted, the request will fail with ErrCompacted .
+ // When passed WithLimit(limit), the number of returned keys is bounded by limit.
+ GetStream(ctx context.Context, key string, opts ...OpOption) (GetStreamChan, error)
+
// Delete deletes a key, or optionally using WithRange(end), [key, end).
Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)
@@ -65,6 +78,35 @@ type KV interface {
Txn(ctx context.Context) Txn
}
+// RangeStreamResponse holds a single chunk from a RangeStream RPC. Kvs is
+// disjoint per chunk; Header, More, and Count are populated only on the
+// final chunk.
+//
+// On a non-EOF stream error, the final value sent on the channel is a
+// terminal RangeStreamResponse with RangeResponse == nil and a non-nil
+// Err().
+type RangeStreamResponse struct {
+ *pb.RangeResponse
+ closeErr error
+}
+
+// Err returns the error value if this RangeStreamResponse is the terminal
+// error response for a stream that failed mid-flight.
+func (r *RangeStreamResponse) Err() error {
+ return r.closeErr
+}
+
+func GetStreamToGetResponse(stream GetStreamChan) (*GetResponse, error) {
+ resp := &pb.RangeResponse{}
+ for r := range stream {
+ if err := r.Err(); err != nil {
+ return nil, err
+ }
+ proto.Merge(resp, r.RangeResponse)
+ }
+ return (*GetResponse)(resp), nil
+}
+
type OpResponse struct {
put *PutResponse
get *GetResponse
@@ -124,6 +166,29 @@ func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetRespon
return r.get, ContextError(ctx, err)
}
+func (kv *kv) GetStream(ctx context.Context, key string, opts ...OpOption) (GetStreamChan, error) {
+ op := OpGet(key, opts...)
+ c, err := kv.remote.RangeStream(ctx, op.toRangeRequest(), kv.callOpts...)
+ if err != nil {
+ return nil, ContextError(ctx, err)
+ }
+ respCh := make(chan RangeStreamResponse, 1)
+ go func() {
+ defer close(respCh)
+ for {
+ resp, err := c.Recv()
+ if err != nil {
+ if !errors.Is(err, io.EOF) {
+ respCh <- RangeStreamResponse{closeErr: ContextError(ctx, err)}
+ }
+ return
+ }
+ respCh <- RangeStreamResponse{RangeResponse: resp.RangeResponse}
+ }
+ }()
+ return respCh, nil
+}
+
func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
r, err := kv.Do(ctx, OpDelete(key, opts...))
return r.del, ContextError(ctx, err)
@@ -134,7 +199,7 @@ func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*C
if err != nil {
return nil, ContextError(ctx, err)
}
- return (*CompactResponse)(resp), err
+ return (*CompactResponse)(resp), nil
}
func (kv *kv) Txn(ctx context.Context) Txn {
diff --git a/client/v3/lease.go b/client/v3/lease.go
index 11b583482863..ab5c559a5487 100644
--- a/client/v3/lease.go
+++ b/client/v3/lease.go
@@ -204,7 +204,7 @@ func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout
l.firstKeepAliveTimeout = defaultTTL
}
if c != nil {
- l.lg = c.lg
+ l.lg = c.GetLogger()
l.callOpts = c.callOpts
}
reqLeaderCtx := WithRequireLeader(context.Background())
diff --git a/client/v3/leasing/cache.go b/client/v3/leasing/cache.go
index 214ee2fc196d..779039d85da8 100644
--- a/client/v3/leasing/cache.go
+++ b/client/v3/leasing/cache.go
@@ -217,26 +217,22 @@ func (lc *leaseCache) Get(ctx context.Context, op v3.Op) (*v3.GetResponse, bool)
}
func (lk *leaseKey) get(op v3.Op) *v3.GetResponse {
- ret := *lk.response
- ret.Header = copyHeader(ret.Header)
- empty := len(ret.Kvs) == 0 || op.IsCountOnly()
- empty = empty || (op.MinModRev() > ret.Kvs[0].ModRevision)
- empty = empty || (op.MaxModRev() != 0 && op.MaxModRev() < ret.Kvs[0].ModRevision)
- empty = empty || (op.MinCreateRev() > ret.Kvs[0].CreateRevision)
- empty = empty || (op.MaxCreateRev() != 0 && op.MaxCreateRev() < ret.Kvs[0].CreateRevision)
+ resp := lk.response
+ empty := len(resp.Kvs) == 0 || op.IsCountOnly()
+ empty = empty || (op.MinModRev() > resp.Kvs[0].ModRevision)
+ empty = empty || (op.MaxModRev() != 0 && op.MaxModRev() < resp.Kvs[0].ModRevision)
+ empty = empty || (op.MinCreateRev() > resp.Kvs[0].CreateRevision)
+ empty = empty || (op.MaxCreateRev() != 0 && op.MaxCreateRev() < resp.Kvs[0].CreateRevision)
+
+ ret := copyGetResponseMetadataOnly(resp)
if empty {
ret.Kvs = nil
} else {
- kv := *ret.Kvs[0]
- kv.Key = make([]byte, len(kv.Key))
- copy(kv.Key, ret.Kvs[0].Key)
- if !op.IsKeysOnly() {
- kv.Value = make([]byte, len(kv.Value))
- copy(kv.Value, ret.Kvs[0].Value)
+ ret.Kvs = []*mvccpb.KeyValue{
+ copyKeyValue(resp.Kvs[0], op.IsKeysOnly()),
}
- ret.Kvs = []*mvccpb.KeyValue{&kv}
}
- return &ret
+ return ret
}
func (lc *leaseCache) notify(key string) (*leaseKey, <-chan struct{}) {
@@ -267,10 +263,10 @@ func (lc *leaseCache) clearOldRevokes(ctx context.Context) {
func (lc *leaseCache) evalCmp(cmps []v3.Cmp) (cmpVal bool, ok bool) {
for _, cmp := range cmps {
- if len(cmp.RangeEnd) > 0 {
+ if len(cmp.GetCompare().GetRangeEnd()) > 0 {
return false, false
}
- lk := lc.entries[string(cmp.Key)]
+ lk := lc.entries[string(cmp.KeyBytes())]
if lk == nil {
return false, false
}
diff --git a/client/v3/leasing/kv.go b/client/v3/leasing/kv.go
index c14af78d629c..febe18ea6efd 100644
--- a/client/v3/leasing/kv.go
+++ b/client/v3/leasing/kv.go
@@ -24,7 +24,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
- pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
v3 "go.etcd.io/etcd/client/v3"
@@ -91,6 +90,11 @@ func (lkv *leasingKV) Put(ctx context.Context, key, val string, opts ...v3.OpOpt
return lkv.put(ctx, v3.OpPut(key, val, opts...))
}
+// GetStream is not supported by leasingKV.
+func (lkv *leasingKV) GetStream(ctx context.Context, key string, opts ...v3.OpOption) (v3.GetStreamChan, error) {
+ return nil, status.Error(codes.Unimplemented, "GetStream is not supported by leasingKV")
+}
+
func (lkv *leasingKV) Delete(ctx context.Context, key string, opts ...v3.OpOption) (*v3.DeleteResponse, error) {
return lkv.delete(ctx, v3.OpDelete(key, opts...))
}
@@ -263,7 +267,7 @@ func (lkv *leasingKV) acquire(ctx context.Context, key string, op v3.Op) (*v3.Tx
if err := lkv.waitSession(ctx); err != nil {
return nil, err
}
- lcmp := v3.Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
+ lcmp := v3.LeaseValue(key)
resp, err := lkv.kv.Txn(ctx).If(
v3.Compare(v3.CreateRevision(lkv.pfx+key), "=", 0),
v3.Compare(lcmp, "=", 0)).
diff --git a/client/v3/leasing/txn.go b/client/v3/leasing/txn.go
index 30c6aa2e4d77..ae7528d4595f 100644
--- a/client/v3/leasing/txn.go
+++ b/client/v3/leasing/txn.go
@@ -206,7 +206,7 @@ func (txn *txnLeasing) serverTxn() (*v3.TxnResponse, error) {
resp, err := txn.lkv.kv.Txn(txn.ctx).If(cmps...).Then(userTxn).Else(fbOps...).Commit()
if err != nil {
for _, cmp := range cmps {
- txn.lkv.leases.Evict(strings.TrimPrefix(string(cmp.Key), txn.lkv.pfx))
+ txn.lkv.leases.Evict(strings.TrimPrefix(string(cmp.KeyBytes()), txn.lkv.pfx))
}
return nil, err
}
diff --git a/client/v3/leasing/util.go b/client/v3/leasing/util.go
index b6a520f03f0c..6d9ef7f6a99c 100644
--- a/client/v3/leasing/util.go
+++ b/client/v3/leasing/util.go
@@ -18,6 +18,7 @@ import (
"bytes"
v3pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
)
@@ -36,26 +37,27 @@ func evalCmp(resp *v3.GetResponse, tcmp v3.Cmp) bool {
var result int
if len(resp.Kvs) != 0 {
kv := resp.Kvs[0]
- switch tcmp.Target {
+ cmp := tcmp.GetCompare()
+ switch cmp.GetTarget() {
case v3pb.Compare_VALUE:
- if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_Value); tv != nil {
+ if tv, _ := cmp.GetTargetUnion().(*v3pb.Compare_Value); tv != nil {
result = bytes.Compare(kv.Value, tv.Value)
}
case v3pb.Compare_CREATE:
- if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_CreateRevision); tv != nil {
+ if tv, _ := cmp.GetTargetUnion().(*v3pb.Compare_CreateRevision); tv != nil {
result = compareInt64(kv.CreateRevision, tv.CreateRevision)
}
case v3pb.Compare_MOD:
- if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_ModRevision); tv != nil {
+ if tv, _ := cmp.GetTargetUnion().(*v3pb.Compare_ModRevision); tv != nil {
result = compareInt64(kv.ModRevision, tv.ModRevision)
}
case v3pb.Compare_VERSION:
- if tv, _ := tcmp.TargetUnion.(*v3pb.Compare_Version); tv != nil {
+ if tv, _ := cmp.GetTargetUnion().(*v3pb.Compare_Version); tv != nil {
result = compareInt64(kv.Version, tv.Version)
}
}
}
- switch tcmp.Result {
+ switch tcmp.GetCompare().GetResult() {
case v3pb.Compare_EQUAL:
return result == 0
case v3pb.Compare_NOT_EQUAL:
@@ -97,8 +99,53 @@ func gatherResponseOps(resp []*v3pb.ResponseOp, ops []v3.Op) (ret []v3.Op) {
}
func copyHeader(hdr *v3pb.ResponseHeader) *v3pb.ResponseHeader {
- h := *hdr
- return &h
+ if hdr == nil {
+ return nil
+ }
+
+ return &v3pb.ResponseHeader{
+ ClusterId: hdr.GetClusterId(),
+ MemberId: hdr.GetMemberId(),
+ Revision: hdr.GetRevision(),
+ RaftTerm: hdr.GetRaftTerm(),
+ }
+}
+
+func copyGetResponseMetadataOnly(resp *v3.GetResponse) *v3.GetResponse {
+ if resp == nil {
+ return nil
+ }
+
+ return &v3.GetResponse{
+ Header: copyHeader(resp.Header),
+ Kvs: nil,
+ More: resp.More,
+ Count: resp.Count,
+ }
+}
+
+func copyKeyValue(kv *mvccpb.KeyValue, keysOnly bool) *mvccpb.KeyValue {
+ if kv == nil {
+ return nil
+ }
+
+ key := make([]byte, len(kv.Key))
+ copy(key, kv.Key)
+
+ var value []byte
+ if !keysOnly {
+ value = make([]byte, len(kv.Value))
+ copy(value, kv.Value)
+ }
+
+ return &mvccpb.KeyValue{
+ Key: key,
+ CreateRevision: kv.CreateRevision,
+ ModRevision: kv.ModRevision,
+ Version: kv.Version,
+ Value: value,
+ Lease: kv.Lease,
+ }
}
func closeAll(chs []chan<- struct{}) {
diff --git a/client/v3/leasing/util_test.go b/client/v3/leasing/util_test.go
new file mode 100644
index 000000000000..110ae505c4f8
--- /dev/null
+++ b/client/v3/leasing/util_test.go
@@ -0,0 +1,173 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 leasing
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ v3pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ v3 "go.etcd.io/etcd/client/v3"
+)
+
+func TestCopyHeader(t *testing.T) {
+ t.Run("ResponseHeader should have 4 protobuf fields", func(t *testing.T) {
+ require.Equal(t, 4, countProtobufFields(&v3pb.ResponseHeader{}))
+ })
+
+ t.Run("nil header", func(t *testing.T) {
+ require.Nil(t, copyHeader(nil))
+ })
+
+ t.Run("copy header", func(t *testing.T) {
+ want := &v3pb.ResponseHeader{
+ ClusterId: 123,
+ MemberId: 456,
+ Revision: 789,
+ RaftTerm: 101112,
+ }
+ actual := copyHeader(want)
+ require.Equal(t, want, actual)
+
+ actual.ClusterId = 999
+ require.Equal(t, uint64(123), want.ClusterId)
+ })
+}
+
+func TestCopyGetResponseMetadataOnly(t *testing.T) {
+ t.Run("GetResponse should have 4 protobuf fields", func(t *testing.T) {
+ require.Equal(t, 4, countProtobufFields(&v3.GetResponse{}))
+ })
+
+ t.Run("nil GetResponse", func(t *testing.T) {
+ require.Nil(t, copyGetResponseMetadataOnly(nil))
+ })
+
+ t.Run("copy GetResponse metadata only", func(t *testing.T) {
+ want := &v3.GetResponse{
+ Header: &v3pb.ResponseHeader{
+ ClusterId: 123,
+ MemberId: 456,
+ Revision: 789,
+ RaftTerm: 101112,
+ },
+ Kvs: []*mvccpb.KeyValue{
+ {
+ Key: []byte("key1"),
+ Value: []byte("value1"),
+ CreateRevision: 1,
+ ModRevision: 2,
+ Version: 3,
+ },
+ },
+ More: true,
+ Count: 1,
+ }
+ actual := copyGetResponseMetadataOnly(want)
+ require.Equal(t, want.Header, actual.Header)
+ require.Nil(t, actual.Kvs)
+ require.True(t, actual.More)
+ require.Equal(t, int64(1), actual.Count)
+
+ actual.Header.ClusterId = 999
+ require.Equal(t, uint64(123), want.Header.ClusterId)
+
+ actual.Count = 2
+ require.Equal(t, int64(1), want.Count)
+ })
+}
+
+func TestCopyKeyValue(t *testing.T) {
+ t.Run("KeyValue should have 6 protobuf fields", func(t *testing.T) {
+ require.Equal(t, 6, countProtobufFields(&mvccpb.KeyValue{}))
+ })
+
+ t.Run("nil key-value", func(t *testing.T) {
+ require.Nil(t, copyKeyValue(nil, false))
+ require.Nil(t, copyKeyValue(nil, true))
+ })
+
+ t.Run("copy key and value", func(t *testing.T) {
+ src := &mvccpb.KeyValue{
+ Key: []byte("key1"),
+ Value: []byte("value1"),
+ CreateRevision: 1,
+ ModRevision: 2,
+ Version: 3,
+ Lease: 4,
+ }
+
+ got := copyKeyValue(src, false)
+ require.NotSame(t, src, got)
+ require.Equal(t, src, got)
+
+ got.Key[0] = 'K'
+ got.Value[0] = 'V'
+ require.Equal(t, byte('k'), src.Key[0])
+ require.Equal(t, byte('v'), src.Value[0])
+
+ src.Key[1] = 'X'
+ src.Value[1] = 'Y'
+ require.Equal(t, byte('e'), got.Key[1])
+ require.Equal(t, byte('a'), got.Value[1])
+ })
+
+ t.Run("keys only", func(t *testing.T) {
+ src := &mvccpb.KeyValue{
+ Key: []byte("key2"),
+ Value: []byte("value2"),
+ CreateRevision: 5,
+ ModRevision: 6,
+ Version: 7,
+ Lease: 8,
+ }
+
+ got := copyKeyValue(src, true)
+ require.Equal(t, &mvccpb.KeyValue{
+ Key: []byte("key2"),
+ Value: nil,
+ CreateRevision: 5,
+ ModRevision: 6,
+ Version: 7,
+ Lease: 8,
+ }, got)
+ require.Nil(t, got.Value)
+
+ got.Key[0] = 'K'
+ require.Equal(t, byte('k'), src.Key[0])
+ })
+}
+
+func countProtobufFields(v any) int {
+ t := reflect.TypeOf(v)
+ if t.Kind() == reflect.Pointer {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Struct {
+ return 0
+ }
+
+ count := 0
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if tag := f.Tag.Get("protobuf"); tag != "" {
+ count++
+ }
+ }
+ return count
+}
diff --git a/client/v3/maintenance.go b/client/v3/maintenance.go
index 00aaacd15fdc..d76ae231feda 100644
--- a/client/v3/maintenance.go
+++ b/client/v3/maintenance.go
@@ -113,7 +113,7 @@ type maintenance struct {
func NewMaintenance(c *Client) Maintenance {
api := &maintenance{
- lg: c.lg,
+ lg: c.GetLogger(),
dial: func(endpoint string) (pb.MaintenanceClient, func(), error) {
conn, err := c.Dial(endpoint)
if err != nil {
@@ -140,7 +140,7 @@ func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client)
}
if c != nil {
api.callOpts = c.callOpts
- api.lg = c.lg
+ api.lg = c.GetLogger()
}
return api
}
diff --git a/client/v3/mirror/syncer.go b/client/v3/mirror/syncer.go
index 55775dc51355..082b57620d1b 100644
--- a/client/v3/mirror/syncer.go
+++ b/client/v3/mirror/syncer.go
@@ -29,7 +29,7 @@ const (
type Syncer interface {
// SyncBase syncs the base state of the key-value state.
// The key-value state are sent through the returned chan.
- SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error)
+ SyncBase(ctx context.Context) (<-chan *clientv3.GetResponse, chan error)
// SyncUpdates syncs the updates of the key-value state.
// The update events are sent through the returned chan.
SyncUpdates(ctx context.Context) clientv3.WatchChan
@@ -46,8 +46,8 @@ type syncer struct {
prefix string
}
-func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, chan error) {
- respchan := make(chan clientv3.GetResponse, 1024)
+func (s *syncer) SyncBase(ctx context.Context) (<-chan *clientv3.GetResponse, chan error) {
+ respchan := make(chan *clientv3.GetResponse, 1024)
errchan := make(chan error, 1)
// if rev is not specified, we will choose the most recent revision.
@@ -99,7 +99,7 @@ func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, cha
return
}
- respchan <- *resp
+ respchan <- resp
if !resp.More {
return
diff --git a/client/v3/mock/mockserver/mockserver.go b/client/v3/mock/mockserver/mockserver.go
index 0467cfd5df55..960900f74ea7 100644
--- a/client/v3/mock/mockserver/mockserver.go
+++ b/client/v3/mock/mockserver/mockserver.go
@@ -22,7 +22,9 @@ import (
"sync"
"google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/resolver"
+ "google.golang.org/grpc/status"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
@@ -165,7 +167,10 @@ func (ms *MockServers) Stop() {
ms.wg.Wait()
}
-type mockKVServer struct{}
+type mockKVServer struct {
+ // we want compile errors if new methods are added
+ pb.UnsafeKVServer
+}
func (m *mockKVServer) Range(context.Context, *pb.RangeRequest) (*pb.RangeResponse, error) {
return &pb.RangeResponse{}, nil
@@ -183,6 +188,10 @@ func (m *mockKVServer) Txn(context.Context, *pb.TxnRequest) (*pb.TxnResponse, er
return &pb.TxnResponse{}, nil
}
+func (m *mockKVServer) RangeStream(*pb.RangeRequest, grpc.ServerStreamingServer[pb.RangeStreamResponse]) error {
+ return status.Error(codes.Unimplemented, "RangeStream is not supported by the mock server")
+}
+
func (m *mockKVServer) Compact(context.Context, *pb.CompactionRequest) (*pb.CompactionResponse, error) {
return &pb.CompactionResponse{}, nil
}
@@ -191,7 +200,10 @@ func (m *mockKVServer) Lease(context.Context, *pb.LeaseGrantRequest) (*pb.LeaseG
return &pb.LeaseGrantResponse{}, nil
}
-type mockLeaseServer struct{}
+type mockLeaseServer struct {
+ // we want compile errors if new methods are added
+ pb.UnsafeLeaseServer
+}
func (s mockLeaseServer) LeaseGrant(context.Context, *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
return &pb.LeaseGrantResponse{}, nil
diff --git a/client/v3/namespace/kv.go b/client/v3/namespace/kv.go
index aa338d5356d8..3b1cebe4ebc8 100644
--- a/client/v3/namespace/kv.go
+++ b/client/v3/namespace/kv.go
@@ -17,6 +17,9 @@ package namespace
import (
"context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
@@ -64,6 +67,11 @@ func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOpti
return get, nil
}
+// GetStream is not supported by kvPrefix.
+func (kv *kvPrefix) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (clientv3.GetStreamChan, error) {
+ return nil, status.Error(codes.Unimplemented, "GetStream is not supported by kvPrefix")
+}
+
func (kv *kvPrefix) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
return nil, rpctypes.ErrEmptyKey
@@ -191,11 +199,11 @@ func (kv *kvPrefix) prefixInterval(key, end []byte) (pfxKey []byte, pfxEnd []byt
func (kv *kvPrefix) prefixCmps(cs []clientv3.Cmp) []clientv3.Cmp {
newCmps := make([]clientv3.Cmp, len(cs))
for i := range cs {
- newCmps[i] = cs[i]
- pfxKey, endKey := kv.prefixInterval(cs[i].KeyBytes(), cs[i].RangeEnd)
+ newCmps[i] = cs[i].Clone()
+ pfxKey, endKey := kv.prefixInterval(cs[i].KeyBytes(), cs[i].GetCompare().GetRangeEnd())
newCmps[i].WithKeyBytes(pfxKey)
- if len(cs[i].RangeEnd) != 0 {
- newCmps[i].RangeEnd = endKey
+ if len(cs[i].GetCompare().GetRangeEnd()) != 0 {
+ newCmps[i].GetCompare().RangeEnd = endKey
}
}
return newCmps
diff --git a/client/v3/naming/doc.go b/client/v3/naming/doc.go
index f2050a6aa6c2..48409b3df01f 100644
--- a/client/v3/naming/doc.go
+++ b/client/v3/naming/doc.go
@@ -39,7 +39,9 @@
// func etcdDial(c *clientv3.Client, service string) (*grpc.ClientConn, error) {
// etcdResolver, err := resolver.NewBuilder(c);
// if err { return nil, err }
-// return grpc.Dial("etcd:///" + service, grpc.WithResolvers(etcdResolver))
+// conn, err := grpc.NewClient("etcd:///"+service, grpc.WithResolvers(etcdResolver))
+// if err != nil { return nil, err }
+// return conn, nil
// }
//
// Optionally, force delete an endpoint:
diff --git a/client/v3/op.go b/client/v3/op.go
index 20cb34f5c3c9..9542f0ccf744 100644
--- a/client/v3/op.go
+++ b/client/v3/op.go
@@ -55,7 +55,8 @@ type Op struct {
// fragmentation should be disabled by default
// if true, split watch events when total exceeds
// "--max-request-bytes" flag value + 512-byte
- fragment bool
+ fragment bool
+ watchBufLogEnabled bool
// for put
ignoreValue bool
@@ -127,10 +128,31 @@ func (op Op) IsKeysOnly() bool { return op.keysOnly }
// IsCountOnly returns whether countOnly is set.
func (op Op) IsCountOnly() bool { return op.countOnly }
+// IsSortSet returns true if WithSort is set.
+func (op Op) IsSortSet() bool { return op.sort != nil }
+
func (op Op) IsOptsWithFromKey() bool { return op.isOptsWithFromKey }
func (op Op) IsOptsWithPrefix() bool { return op.isOptsWithPrefix }
+// IsPrevKV returns whether WithPrevKV() is set.
+func (op Op) IsPrevKV() bool { return op.prevKV }
+
+// IsFragment returns whether WithFragment() is set.
+func (op Op) IsFragment() bool { return op.fragment }
+
+// IsProgressNotify returns whether WithProgressNotify() is set.
+func (op Op) IsProgressNotify() bool { return op.progressNotify }
+
+// IsCreatedNotify returns whether WithCreatedNotify() is set.
+func (op Op) IsCreatedNotify() bool { return op.createdNotify }
+
+// IsFilterPut returns whether WithFilterPut() is set.
+func (op Op) IsFilterPut() bool { return op.filterPut }
+
+// IsFilterDelete returns whether WithFilterDelete() is set.
+func (op Op) IsFilterDelete() bool { return op.filterDelete }
+
// MinModRev returns the operation's minimum modify revision.
func (op Op) MinModRev() int64 { return op.minModRev }
@@ -187,7 +209,8 @@ func (op Op) toTxnRequest() *pb.TxnRequest {
}
cmps := make([]*pb.Compare, len(op.cmps))
for i := range op.cmps {
- cmps[i] = (*pb.Compare)(&op.cmps[i])
+ cmp := op.cmps[i].Clone()
+ cmps[i] = cmp.GetCompare()
}
return &pb.TxnRequest{Compare: cmps, Success: thenOps, Failure: elseOps}
}
@@ -305,10 +328,14 @@ func OpPut(key, val string, opts ...OpOption) Op {
// OpTxn returns "txn" operation based on given transaction conditions.
func OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {
- return Op{t: tTxn, cmps: cmps, thenOps: thenOps, elseOps: elseOps}
+ clonedCmps := make([]Cmp, len(cmps))
+ for i := range cmps {
+ clonedCmps[i] = cmps[i].Clone()
+ }
+ return Op{t: tTxn, cmps: clonedCmps, thenOps: thenOps, elseOps: elseOps}
}
-func opWatch(key string, opts ...OpOption) Op {
+func OpWatch(key string, opts ...OpOption) Op {
ret := Op{t: tRange, key: []byte(key)}
ret.applyOpts(opts)
switch {
@@ -529,6 +556,11 @@ func WithFragment() OpOption {
return func(op *Op) { op.fragment = true }
}
+// WithWatchBufLog enables watch response buffer logging.
+func WithWatchBufLog() OpOption {
+ return func(op *Op) { op.watchBufLogEnabled = true }
+}
+
// WithIgnoreValue updates the key using its current value.
// This option can not be combined with non-empty values.
// Returns an error if the key does not exist.
diff --git a/client/v3/ordering/kv.go b/client/v3/ordering/kv.go
index 9075cbf9890c..741feebb80a6 100644
--- a/client/v3/ordering/kv.go
+++ b/client/v3/ordering/kv.go
@@ -18,6 +18,9 @@ import (
"context"
"sync"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
clientv3 "go.etcd.io/etcd/client/v3"
)
@@ -31,7 +34,7 @@ type kvOrdering struct {
revMu sync.RWMutex
}
-func NewKV(kv clientv3.KV, orderViolationFunc OrderViolationFunc) *kvOrdering {
+func NewKV(kv clientv3.KV, orderViolationFunc OrderViolationFunc) clientv3.KV {
return &kvOrdering{kv, orderViolationFunc, 0, sync.RWMutex{}}
}
@@ -75,6 +78,11 @@ func (kv *kvOrdering) Get(ctx context.Context, key string, opts ...clientv3.OpOp
}
}
+// GetStream is not supported by kvOrdering.
+func (kv *kvOrdering) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (clientv3.GetStreamChan, error) {
+ return nil, status.Error(codes.Unimplemented, "GetStream is not supported by kvOrdering")
+}
+
func (kv *kvOrdering) Txn(ctx context.Context) clientv3.Txn {
return &txnOrdering{
kv.KV.Txn(ctx),
diff --git a/client/v3/ordering/kv_test.go b/client/v3/ordering/kv_test.go
index cb6d0d3909e4..679d74771ad0 100644
--- a/client/v3/ordering/kv_test.go
+++ b/client/v3/ordering/kv_test.go
@@ -76,7 +76,7 @@ func TestKvOrdering(t *testing.T) {
tt.prevRev,
sync.RWMutex{},
}
- res, err := kv.Get(context.TODO(), "mockKey")
+ res, err := kv.Get(t.Context(), "mockKey")
if err != nil {
t.Errorf("#%d: expected response %+v, got error %+v", i, tt.response, err)
}
@@ -131,9 +131,9 @@ func TestTxnOrdering(t *testing.T) {
sync.RWMutex{},
}
txn := &txnOrdering{
- kv.Txn(context.Background()),
+ kv.Txn(t.Context()),
kv,
- context.Background(),
+ t.Context(),
sync.Mutex{},
[]clientv3.Cmp{},
[]clientv3.Op{},
diff --git a/client/v3/retry.go b/client/v3/retry.go
index 9152c53a7d4c..d4263c4b281e 100644
--- a/client/v3/retry.go
+++ b/client/v3/retry.go
@@ -108,6 +108,10 @@ func (rkv *retryKVClient) Range(ctx context.Context, in *pb.RangeRequest, opts .
return rkv.kc.Range(ctx, in, append(opts, withRepeatablePolicy())...)
}
+func (rkv *retryKVClient) RangeStream(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (pb.KV_RangeStreamClient, error) {
+ return rkv.kc.RangeStream(ctx, in, opts...)
+}
+
func (rkv *retryKVClient) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (resp *pb.PutResponse, err error) {
return rkv.kc.Put(ctx, in, opts...)
}
diff --git a/client/v3/retry_interceptor.go b/client/v3/retry_interceptor.go
index 9b4bd0219b41..18aa3ae80a7c 100644
--- a/client/v3/retry_interceptor.go
+++ b/client/v3/retry_interceptor.go
@@ -150,6 +150,11 @@ func (c *Client) streamClientInterceptor(optFuncs ...retryOption) grpc.StreamCli
// shouldRefreshToken checks whether there's a need to refresh the token based on the error and callOptions,
// and returns a boolean value.
func (c *Client) shouldRefreshToken(err error, callOpts *options) bool {
+ if c.Token != "" {
+ // do not try to refresh the token as it is set by user
+ return false
+ }
+
if errors.Is(rpctypes.Error(err), rpctypes.ErrUserEmpty) {
// refresh the token when username, password is present but the server returns ErrUserEmpty
// which is possible when the client token is cleared somehow
@@ -239,12 +244,12 @@ func (s *serverStreamingRetryingStream) RecvMsg(m any) error {
}
newStream, err := s.reestablishStreamAndResendBuffer(s.ctx)
if err != nil {
- s.client.lg.Error("failed reestablishStreamAndResendBuffer", zap.Error(err))
+ s.client.GetLogger().Error("failed reestablishStreamAndResendBuffer", zap.Error(err))
return err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
}
s.setStream(newStream)
- s.client.lg.Warn("retrying RecvMsg", zap.Error(lastErr))
+ s.client.GetLogger().Warn("retrying RecvMsg", zap.Error(lastErr))
attemptRetry, lastErr = s.receiveMsgAndIndicateRetry(m)
if !attemptRetry {
return lastErr
@@ -277,7 +282,7 @@ func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m any) (bool,
if s.client.shouldRefreshToken(err, s.callOpts) {
gtErr := s.client.refreshToken(s.ctx)
if gtErr != nil {
- s.client.lg.Warn("retry failed to fetch new auth token", zap.Error(gtErr))
+ s.client.GetLogger().Warn("retry failed to fetch new auth token", zap.Error(gtErr))
return false, err // return the original error for simplicity
}
return true, err
@@ -343,7 +348,7 @@ func isSafeRetry(c *Client, err error, callOpts *options) bool {
case nonRepeatable:
return isSafeRetryMutableRPC(err)
default:
- c.lg.Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
+ c.GetLogger().Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
return false
}
}
diff --git a/client/v3/retry_interceptor_test.go b/client/v3/retry_interceptor_test.go
index 393bb9bbe5cb..323704e7f2e8 100644
--- a/client/v3/retry_interceptor_test.go
+++ b/client/v3/retry_interceptor_test.go
@@ -35,6 +35,7 @@ func (d dummyAuthTokenBundle) UpdateAuthToken(token string) {
func TestClientShouldRefreshToken(t *testing.T) {
type fields struct {
authTokenBundle credentials.PerRPCCredentialsBundle
+ token string
}
type args struct {
err error
@@ -118,11 +119,21 @@ func TestClientShouldRefreshToken(t *testing.T) {
args: args{rpctypes.ErrGRPCAuthFailed, optsWithFalse},
want: false,
},
+ {
+ name: "User provided token, ErrGRPCInvalidAuthToken",
+ fields: fields{
+ authTokenBundle: nil,
+ token: "user-supplied-token",
+ },
+ args: args{rpctypes.ErrGRPCInvalidAuthToken, optsWithTrue},
+ want: false,
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
authTokenBundle: tt.fields.authTokenBundle,
+ Token: tt.fields.token,
}
if got := c.shouldRefreshToken(tt.args.err, tt.args.callOpts); got != tt.want {
t.Errorf("shouldRefreshToken() = %v, want %v", got, tt.want)
diff --git a/client/v3/txn.go b/client/v3/txn.go
index 0a57332ac789..152016b34ac9 100644
--- a/client/v3/txn.go
+++ b/client/v3/txn.go
@@ -89,7 +89,8 @@ func (txn *txn) If(cs ...Cmp) Txn {
txn.cif = true
for i := range cs {
- txn.cmps = append(txn.cmps, (*pb.Compare)(&cs[i]))
+ cmp := cs[i].Clone()
+ txn.cmps = append(txn.cmps, cmp.GetCompare())
}
return txn
diff --git a/client/v3/txn_test.go b/client/v3/txn_test.go
index de7aec3b8e98..60687219d600 100644
--- a/client/v3/txn_test.go
+++ b/client/v3/txn_test.go
@@ -15,7 +15,6 @@
package clientv3
import (
- "context"
"testing"
"time"
@@ -44,7 +43,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).If(cmp).If(cmp)
+ kv.Txn(t.Context()).If(cmp).If(cmp)
},
err: "cannot call If twice!",
@@ -52,7 +51,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).Then(op).If(cmp)
+ kv.Txn(t.Context()).Then(op).If(cmp)
},
err: "cannot call If after Then!",
@@ -60,7 +59,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).Else(op).If(cmp)
+ kv.Txn(t.Context()).Else(op).If(cmp)
},
err: "cannot call If after Else!",
@@ -68,7 +67,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).Then(op).Then(op)
+ kv.Txn(t.Context()).Then(op).Then(op)
},
err: "cannot call Then twice!",
@@ -76,7 +75,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).Else(op).Then(op)
+ kv.Txn(t.Context()).Else(op).Then(op)
},
err: "cannot call Then after Else!",
@@ -84,7 +83,7 @@ func TestTxnPanics(t *testing.T) {
{
f: func(errc chan string) {
defer df(errc)
- kv.Txn(context.TODO()).Else(op).Else(op)
+ kv.Txn(t.Context()).Else(op).Else(op)
},
err: "cannot call Else twice!",
diff --git a/client/v3/watch.go b/client/v3/watch.go
index a46f98b8e287..46adce7ba84d 100644
--- a/client/v3/watch.go
+++ b/client/v3/watch.go
@@ -33,8 +33,8 @@ import (
)
const (
- EventTypeDelete = mvccpb.DELETE
- EventTypePut = mvccpb.PUT
+ EventTypeDelete = mvccpb.Event_DELETE
+ EventTypePut = mvccpb.Event_PUT
closeSendErrTimeout = 250 * time.Millisecond
@@ -44,12 +44,22 @@ const (
// InvalidWatchID represents an invalid watch ID and prevents duplication with an existing watch.
InvalidWatchID = -1
+
+ watcherStreamBufWarningInterval = 5 * time.Second
+ watcherStreamBufWarningThreshold = 100 * time.Millisecond
)
-type Event mvccpb.Event
+type Event = mvccpb.Event
type WatchChan <-chan WatchResponse
+func ensureWatchHeader(hdr *pb.ResponseHeader) *pb.ResponseHeader {
+ if hdr != nil {
+ return hdr
+ }
+ return &pb.ResponseHeader{}
+}
+
type Watcher interface {
// Watch watches on a key or prefix. The watched events will be returned
// through the returned channel. If revisions waiting to be sent over the
@@ -89,7 +99,7 @@ type Watcher interface {
}
type WatchResponse struct {
- Header pb.ResponseHeader
+ Header *pb.ResponseHeader
Events []*Event
// CompactRevision is the minimum revision the watcher may receive.
@@ -105,18 +115,8 @@ type WatchResponse struct {
closeErr error
- // cancelReason is a reason of canceling watch
- cancelReason string
-}
-
-// IsCreate returns true if the event tells that the key is newly created.
-func (e *Event) IsCreate() bool {
- return e.Type == EventTypePut && e.Kv.CreateRevision == e.Kv.ModRevision
-}
-
-// IsModify returns true if the event tells that a new value is put on existing key.
-func (e *Event) IsModify() bool {
- return e.Type == EventTypePut && e.Kv.CreateRevision != e.Kv.ModRevision
+ // CancelReason is a reason of canceling watch
+ CancelReason string
}
// Err is the error value if this WatchResponse holds an error.
@@ -127,8 +127,8 @@ func (wr *WatchResponse) Err() error {
case wr.CompactRevision != 0:
return v3rpc.ErrCompacted
case wr.Canceled:
- if len(wr.cancelReason) != 0 {
- return v3rpc.Error(status.Error(codes.FailedPrecondition, wr.cancelReason))
+ if len(wr.CancelReason) != 0 {
+ return v3rpc.Error(status.Error(codes.FailedPrecondition, wr.CancelReason))
}
return v3rpc.ErrFutureRev
}
@@ -137,7 +137,7 @@ func (wr *WatchResponse) Err() error {
// IsProgressNotify returns true if the WatchResponse is progress notification.
func (wr *WatchResponse) IsProgressNotify() bool {
- return len(wr.Events) == 0 && !wr.Canceled && !wr.Created && wr.CompactRevision == 0 && wr.Header.Revision != 0
+ return len(wr.Events) == 0 && !wr.Canceled && !wr.Created && wr.CompactRevision == 0 && wr.Header.GetRevision() != 0
}
// watcher implements the Watcher interface
@@ -211,6 +211,8 @@ type watchRequest struct {
// if true, split watch events when total exceeds
// "--max-request-bytes" flag value + 512-byte
fragment bool
+ // watchBufLogEnabled enables watch response buffer logging.
+ watchBufLogEnabled bool
// filters is the list of events to filter out
filters []pb.WatchCreateRequest_FilterType
@@ -241,6 +243,10 @@ type watcherStream struct {
// buf holds all events received from etcd but not yet consumed by the client
buf []*WatchResponse
+ // bufLogger tracks buffer backlog and rate-limits warning logs.
+ bufLogger *blockLogger
+ // bufWaitStartTime is set while the first response in buf is waiting for outc.
+ bufWaitStartTime time.Time
}
func NewWatcher(c *Client) Watcher {
@@ -254,7 +260,7 @@ func NewWatchFromWatchClient(wc pb.WatchClient, c *Client) Watcher {
}
if c != nil {
w.callOpts = c.callOpts
- w.lg = c.lg
+ w.lg = c.GetLogger()
}
return w
}
@@ -296,7 +302,7 @@ func (w *watcher) newWatcherGRPCStream(inctx context.Context) *watchGRPCStream {
// Watch posts a watch request to run() and waits for a new watcher channel
func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan {
- ow := opWatch(key, opts...)
+ ow := OpWatch(key, opts...)
var filters []pb.WatchCreateRequest_FilterType
if ow.filterPut {
@@ -307,16 +313,17 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
}
wr := &watchRequest{
- ctx: ctx,
- createdNotify: ow.createdNotify,
- key: string(ow.key),
- end: string(ow.end),
- rev: ow.rev,
- progressNotify: ow.progressNotify,
- fragment: ow.fragment,
- filters: filters,
- prevKV: ow.prevKV,
- retc: make(chan chan WatchResponse, 1),
+ ctx: ctx,
+ createdNotify: ow.createdNotify,
+ key: string(ow.key),
+ end: string(ow.end),
+ rev: ow.rev,
+ progressNotify: ow.progressNotify,
+ fragment: ow.fragment,
+ watchBufLogEnabled: ow.watchBufLogEnabled,
+ filters: filters,
+ prevKV: ow.prevKV,
+ retc: make(chan chan WatchResponse, 1),
}
ok := false
@@ -356,7 +363,7 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
case <-donec:
ok = false
if wgs.closeErr != nil {
- closeCh <- WatchResponse{Canceled: true, closeErr: wgs.closeErr}
+ closeCh <- WatchResponse{Header: &pb.ResponseHeader{}, Canceled: true, closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
@@ -371,7 +378,7 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
case <-ctx.Done():
case <-donec:
if wgs.closeErr != nil {
- closeCh <- WatchResponse{Canceled: true, closeErr: wgs.closeErr}
+ closeCh <- WatchResponse{Header: &pb.ResponseHeader{}, Canceled: true, closeErr: wgs.closeErr}
break
}
// retry; may have dropped stream from no ctxs
@@ -485,7 +492,7 @@ func (w *watchGRPCStream) closeSubstream(ws *watcherStream) {
}
// close subscriber's channel
if closeErr := w.closeErr; closeErr != nil && ws.initReq.ctx.Err() == nil {
- go w.sendCloseSubstream(ws, &WatchResponse{Canceled: true, closeErr: w.closeErr})
+ go w.sendCloseSubstream(ws, &WatchResponse{Header: &pb.ResponseHeader{}, Canceled: true, closeErr: w.closeErr})
} else if ws.outc != nil {
close(ws.outc)
}
@@ -557,6 +564,9 @@ func (w *watchGRPCStream) run() {
// unbuffered so resumes won't cause repeat events
recvc: make(chan *WatchResponse),
}
+ if ws.initReq.watchBufLogEnabled {
+ ws.bufLogger = w.newWatcherStreamBufLogger(ws, time.Now)
+ }
ws.donec = make(chan struct{})
w.wg.Add(1)
@@ -580,7 +590,7 @@ func (w *watchGRPCStream) run() {
case pbresp := <-w.respc:
if cur == nil || pbresp.Created || pbresp.Canceled {
cur = pbresp
- } else if cur != nil && cur.WatchId == pbresp.WatchId {
+ } else if cur.WatchId == pbresp.WatchId {
// merge new events
cur.Events = append(cur.Events, pbresp.Events...)
// update "Fragment" field; last response with "Fragment" == false
@@ -712,18 +722,14 @@ func (w *watchGRPCStream) nextResume() *watcherStream {
// dispatchEvent sends a WatchResponse to the appropriate watcher stream
func (w *watchGRPCStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
- events := make([]*Event, len(pbresp.Events))
- for i, ev := range pbresp.Events {
- events[i] = (*Event)(ev)
- }
// TODO: return watch ID?
wr := &WatchResponse{
- Header: *pbresp.Header,
- Events: events,
+ Header: ensureWatchHeader(pbresp.Header),
+ Events: pbresp.Events,
CompactRevision: pbresp.CompactRevision,
Created: pbresp.Created,
Canceled: pbresp.Canceled,
- cancelReason: pbresp.CancelReason,
+ CancelReason: pbresp.CancelReason,
}
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of InvalidWatchID to
@@ -798,19 +804,26 @@ func (w *watchGRPCStream) serveSubstream(ws *watcherStream, resumec chan struct{
}
w.wg.Done()
}()
+ defer func() {
+ if !resuming {
+ w.recordBufWait(ws)
+ }
+ }()
- emptyWr := &WatchResponse{}
+ emptyWr := &WatchResponse{Header: &pb.ResponseHeader{}}
for {
curWr := emptyWr
outc := ws.outc
if len(ws.buf) > 0 {
curWr = ws.buf[0]
+ w.startBufWait(ws)
} else {
outc = nil
}
select {
case outc <- *curWr:
+ w.recordBufWait(ws)
if ws.buf[0].Err() != nil {
return
}
@@ -870,6 +883,7 @@ func (w *watchGRPCStream) serveSubstream(ws *watcherStream, resumec chan struct{
case <-ws.initReq.ctx.Done():
return
case <-resumec:
+ ws.resetBufWait()
resuming = true
return
}
@@ -877,6 +891,40 @@ func (w *watchGRPCStream) serveSubstream(ws *watcherStream, resumec chan struct{
// lazily send cancel message if events on missing id
}
+func (w *watchGRPCStream) startBufWait(ws *watcherStream) {
+ if w.lg == nil || ws.bufLogger == nil || !ws.bufWaitStartTime.IsZero() {
+ return
+ }
+ ws.bufWaitStartTime = ws.bufLogger.now()
+}
+
+func (w *watchGRPCStream) recordBufWait(ws *watcherStream) {
+ if w.lg == nil || ws.bufLogger == nil || ws.bufWaitStartTime.IsZero() {
+ return
+ }
+ ws.bufLogger.recordWait(ws.bufLogger.now().Sub(ws.bufWaitStartTime))
+ ws.resetBufWait()
+}
+
+func (ws *watcherStream) resetBufWait() {
+ ws.bufWaitStartTime = time.Time{}
+}
+
+func (w *watchGRPCStream) newWatcherStreamBufLogger(ws *watcherStream, now func() time.Time) *blockLogger {
+ return newBlockLogger(watcherStreamBufWarningInterval, watcherStreamBufWarningThreshold, now, func(responseCount int, timeWaiting time.Duration, window time.Duration) {
+ w.lg.Info(
+ "watcher substream buffer is backlogged; subscriber may be too slow to consume events",
+ zap.String("range-start", ws.initReq.key),
+ zap.String("range-end", ws.initReq.end),
+ zap.Int64("watch-revision", ws.initReq.rev),
+ zap.Int("buffer-size", len(ws.buf)),
+ zap.Int("response-count", responseCount),
+ zap.Duration("time-blocked", timeWaiting),
+ zap.Duration("log-interval", window),
+ )
+ })
+}
+
func (w *watchGRPCStream) newWatchClient() (pb.Watch_WatchClient, error) {
// mark all substreams as resuming
close(w.resumec)
@@ -896,7 +944,7 @@ func (w *watchGRPCStream) newWatchClient() (pb.Watch_WatchClient, error) {
w.resuming = resuming
w.substreams = make(map[int64]*watcherStream)
- // connect to grpc stream while accepting watcher cancelation
+ // connect to grpc stream while accepting watcher cancellation
stopc := make(chan struct{})
donec := w.waitCancelSubstreams(stopc)
wc, err := w.openWatchClient()
diff --git a/client/v3/watch_test.go b/client/v3/watch_test.go
index 721fc4a8a237..255194e73fb0 100644
--- a/client/v3/watch_test.go
+++ b/client/v3/watch_test.go
@@ -16,10 +16,14 @@ package clientv3
import (
"context"
+ "sync"
"testing"
+ "time"
+ "go.uber.org/zap"
"google.golang.org/grpc/metadata"
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
)
@@ -72,7 +76,7 @@ func TestStreamKeyFromCtx(t *testing.T) {
}{
{
name: "multiple keys",
- ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{
+ ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{
"key1": []string{"value1"},
"key2": []string{"value2a", "value2b"},
}),
@@ -80,19 +84,19 @@ func TestStreamKeyFromCtx(t *testing.T) {
},
{
name: "no keys",
- ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{}),
+ ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{}),
expected: "map[]",
},
{
name: "only one key",
- ctx: metadata.NewOutgoingContext(context.Background(), metadata.MD{
+ ctx: metadata.NewOutgoingContext(t.Context(), metadata.MD{
"key1": []string{"value1", "value1a"},
}),
expected: "map[key1:[value1 value1a]]",
},
{
name: "no metadata",
- ctx: context.Background(),
+ ctx: t.Context(),
expected: "",
},
}
@@ -106,3 +110,97 @@ func TestStreamKeyFromCtx(t *testing.T) {
})
}
}
+
+func TestServeSubstreamLogsSlowConsumer(t *testing.T) {
+ const (
+ interval = 5 * time.Second
+ threshold = 100 * time.Millisecond
+ )
+
+ ctx, cancel := context.WithCancel(t.Context())
+ defer cancel()
+
+ var nowMu sync.Mutex
+ now := time.Unix(0, 0)
+ startedWaiting := make(chan struct{})
+ waitStarted := false
+
+ var logMu sync.Mutex
+ logCalls := 0
+ logged := make(chan struct{}, 1)
+
+ ws := &watcherStream{
+ initReq: watchRequest{ctx: ctx},
+ outc: make(chan WatchResponse),
+ recvc: make(chan *WatchResponse, 1),
+ donec: make(chan struct{}),
+ }
+ ws.bufLogger = newBlockLogger(interval, threshold, func() time.Time {
+ nowMu.Lock()
+ defer nowMu.Unlock()
+ if waitStarted {
+ close(startedWaiting)
+ waitStarted = false
+ }
+ return now
+ }, func(eventCount int, timeWaiting time.Duration, window time.Duration) {
+ logMu.Lock()
+ defer logMu.Unlock()
+ logCalls++
+ select {
+ case logged <- struct{}{}:
+ default:
+ }
+ })
+
+ nowMu.Lock()
+ waitStarted = true
+ nowMu.Unlock()
+
+ w := &watchGRPCStream{
+ ctx: t.Context(),
+ closingc: make(chan *watcherStream, 1),
+ lg: zap.NewNop(),
+ }
+ w.wg.Add(1)
+ go w.serveSubstream(ws, make(chan struct{}))
+
+ ws.recvc <- &WatchResponse{Header: &pb.ResponseHeader{Revision: 1}}
+
+ select {
+ case <-startedWaiting:
+ case <-time.After(10 * time.Second):
+ t.Fatal("timed out waiting for serveSubstream to start buffering")
+ }
+
+ nowMu.Lock()
+ now = now.Add(threshold + interval)
+ nowMu.Unlock()
+
+ select {
+ case <-ws.outc:
+ case <-time.After(time.Second):
+ t.Fatal("timed out waiting for buffered response delivery")
+ }
+
+ select {
+ case <-logged:
+ case <-time.After(time.Second):
+ t.Fatal("timed out waiting for backlog warning callback")
+ }
+
+ logMu.Lock()
+ if logCalls != 1 {
+ logMu.Unlock()
+ t.Fatalf("expected one backlog warning, got %d", logCalls)
+ }
+ logMu.Unlock()
+
+ cancel()
+ select {
+ case <-ws.donec:
+ case <-time.After(time.Second):
+ t.Fatal("timed out waiting for serveSubstream shutdown")
+ }
+ w.wg.Wait()
+}
diff --git a/client/v3/yaml/config_test.go b/client/v3/yaml/config_test.go
index 4d23f27494e1..f4c68c999dfe 100644
--- a/client/v3/yaml/config_test.go
+++ b/client/v3/yaml/config_test.go
@@ -73,7 +73,7 @@ func TestConfigFromFile(t *testing.T) {
}
for i, tt := range tests {
- tmpfile, err := os.CreateTemp("", "clientcfg")
+ tmpfile, err := os.CreateTemp(t.TempDir(), "clientcfg")
if err != nil {
log.Fatal(err)
}
diff --git a/contrib/raftexample/httpapi.go b/contrib/raftexample/httpapi.go
index dbe226add33c..96f8265e8916 100644
--- a/contrib/raftexample/httpapi.go
+++ b/contrib/raftexample/httpapi.go
@@ -26,7 +26,7 @@ import (
// Handler for a http based key-value store backed by raft
type httpKVAPI struct {
store *kvstore
- confChangeC chan<- raftpb.ConfChange
+ confChangeC chan<- *raftpb.ConfChange
}
func (h *httpKVAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -68,11 +68,11 @@ func (h *httpKVAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: nodeID,
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(nodeID),
Context: url,
}
- h.confChangeC <- cc
+ h.confChangeC <- &cc
// As above, optimistic that raft will apply the conf change
w.WriteHeader(http.StatusNoContent)
case http.MethodDelete:
@@ -84,10 +84,10 @@ func (h *httpKVAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: nodeID,
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(nodeID),
}
- h.confChangeC <- cc
+ h.confChangeC <- &cc
// As above, optimistic that raft will apply the conf change
w.WriteHeader(http.StatusNoContent)
@@ -101,7 +101,7 @@ func (h *httpKVAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// serveHTTPKVAPI starts a key-value server with a GET/PUT API and listens.
-func serveHTTPKVAPI(kv *kvstore, port int, confChangeC chan<- raftpb.ConfChange, errorC <-chan error) {
+func serveHTTPKVAPI(kv *kvstore, port int, confChangeC chan<- *raftpb.ConfChange, errorC <-chan error) {
srv := http.Server{
Addr: ":" + strconv.Itoa(port),
Handler: &httpKVAPI{
diff --git a/contrib/raftexample/main.go b/contrib/raftexample/main.go
index 73f02787a356..d011d45c9046 100644
--- a/contrib/raftexample/main.go
+++ b/contrib/raftexample/main.go
@@ -30,7 +30,7 @@ func main() {
proposeC := make(chan string)
defer close(proposeC)
- confChangeC := make(chan raftpb.ConfChange)
+ confChangeC := make(chan *raftpb.ConfChange)
defer close(confChangeC)
// raft provides a commit stream for the proposals from the http api
diff --git a/contrib/raftexample/raft.go b/contrib/raftexample/raft.go
index 5e5dc010bbe5..970dad1916f9 100644
--- a/contrib/raftexample/raft.go
+++ b/contrib/raftexample/raft.go
@@ -25,6 +25,9 @@ import (
"strconv"
"time"
+ "go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
@@ -34,8 +37,6 @@ import (
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
"go.etcd.io/raft/v3"
"go.etcd.io/raft/v3/raftpb"
-
- "go.uber.org/zap"
)
type commit struct {
@@ -45,10 +46,10 @@ type commit struct {
// A key-value stream backed by raft
type raftNode struct {
- proposeC <-chan string // proposed messages (k,v)
- confChangeC <-chan raftpb.ConfChange // proposed cluster config changes
- commitC chan<- *commit // entries committed to log (k,v)
- errorC chan<- error // errors from raft session
+ proposeC <-chan string // proposed messages (k,v)
+ confChangeC <-chan *raftpb.ConfChange // proposed cluster config changes
+ commitC chan<- *commit // entries committed to log (k,v)
+ errorC chan<- error // errors from raft session
id int // client ID for raft session
peers []string // raft peer URLs
@@ -57,7 +58,7 @@ type raftNode struct {
snapdir string // path to snapshot directory
getSnapshot func() ([]byte, error)
- confState raftpb.ConfState
+ confState *raftpb.ConfState
snapshotIndex uint64
appliedIndex uint64
@@ -86,7 +87,7 @@ var defaultSnapshotCount uint64 = 10000
// commit channel, followed by a nil message (to indicate the channel is
// current), then new log entries. To shutdown, close proposeC and read errorC.
func newRaftNode(id int, peers []string, join bool, getSnapshot func() ([]byte, error), proposeC <-chan string,
- confChangeC <-chan raftpb.ConfChange,
+ confChangeC <-chan *raftpb.ConfChange,
) (<-chan *commit, <-chan error, <-chan *snap.Snapshotter) {
commitC := make(chan *commit)
errorC := make(chan error)
@@ -116,11 +117,11 @@ func newRaftNode(id int, peers []string, join bool, getSnapshot func() ([]byte,
return commitC, errorC, rc.snapshotterReady
}
-func (rc *raftNode) saveSnap(snap raftpb.Snapshot) error {
+func (rc *raftNode) saveSnap(snap *raftpb.Snapshot) error {
walSnap := walpb.Snapshot{
Index: snap.Metadata.Index,
Term: snap.Metadata.Term,
- ConfState: &snap.Metadata.ConfState,
+ ConfState: snap.Metadata.ConfState,
}
// save the snapshot file before writing the snapshot to the wal.
// This makes it possible for the snapshot file to become orphaned, but prevents
@@ -128,17 +129,17 @@ func (rc *raftNode) saveSnap(snap raftpb.Snapshot) error {
if err := rc.snapshotter.SaveSnap(snap); err != nil {
return err
}
- if err := rc.wal.SaveSnapshot(walSnap); err != nil {
+ if err := rc.wal.SaveSnapshot(&walSnap); err != nil {
return err
}
- return rc.wal.ReleaseLockTo(snap.Metadata.Index)
+ return rc.wal.ReleaseLockTo(snap.Metadata.GetIndex())
}
-func (rc *raftNode) entriesToApply(ents []raftpb.Entry) (nents []raftpb.Entry) {
+func (rc *raftNode) entriesToApply(ents []*raftpb.Entry) (nents []*raftpb.Entry) {
if len(ents) == 0 {
return ents
}
- firstIdx := ents[0].Index
+ firstIdx := ents[0].GetIndex()
if firstIdx > rc.appliedIndex+1 {
log.Fatalf("first index of committed entry[%d] should <= progress.appliedIndex[%d]+1", firstIdx, rc.appliedIndex)
}
@@ -150,14 +151,14 @@ func (rc *raftNode) entriesToApply(ents []raftpb.Entry) (nents []raftpb.Entry) {
// publishEntries writes committed log entries to commit channel and returns
// whether all entries could be published.
-func (rc *raftNode) publishEntries(ents []raftpb.Entry) (<-chan struct{}, bool) {
+func (rc *raftNode) publishEntries(ents []*raftpb.Entry) (<-chan struct{}, bool) {
if len(ents) == 0 {
return nil, true
}
data := make([]string, 0, len(ents))
for i := range ents {
- switch ents[i].Type {
+ switch ents[i].GetType() {
case raftpb.EntryNormal:
if len(ents[i].Data) == 0 {
// ignore empty messages
@@ -167,19 +168,21 @@ func (rc *raftNode) publishEntries(ents []raftpb.Entry) (<-chan struct{}, bool)
data = append(data, s)
case raftpb.EntryConfChange:
var cc raftpb.ConfChange
- cc.Unmarshal(ents[i].Data)
- rc.confState = *rc.node.ApplyConfChange(cc)
- switch cc.Type {
+ if err := proto.Unmarshal(ents[i].Data, &cc); err != nil {
+ log.Fatalf("failed to unmarshal conf change: %v", err)
+ }
+ rc.confState = rc.node.ApplyConfChange(&cc)
+ switch cc.GetType() {
case raftpb.ConfChangeAddNode:
if len(cc.Context) > 0 {
- rc.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
+ rc.transport.AddPeer(types.ID(cc.GetNodeId()), []string{string(cc.Context)})
}
case raftpb.ConfChangeRemoveNode:
- if cc.NodeID == uint64(rc.id) {
+ if cc.GetNodeId() == uint64(rc.id) {
log.Println("I've been removed from the cluster! Shutting down.")
return nil, false
}
- rc.transport.RemovePeer(types.ID(cc.NodeID))
+ rc.transport.RemovePeer(types.ID(cc.GetNodeId()))
}
}
}
@@ -196,7 +199,7 @@ func (rc *raftNode) publishEntries(ents []raftpb.Entry) (<-chan struct{}, bool)
}
// after commit, update appliedIndex
- rc.appliedIndex = ents[len(ents)-1].Index
+ rc.appliedIndex = ents[len(ents)-1].GetIndex()
return applyDoneC, true
}
@@ -231,11 +234,11 @@ func (rc *raftNode) openWAL(snapshot *raftpb.Snapshot) *wal.WAL {
}
walsnap := walpb.Snapshot{}
- if snapshot != nil {
+ if snapshot.GetMetadata() != nil {
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
}
- log.Printf("loading WAL at term %d and index %d", walsnap.Term, walsnap.Index)
- w, err := wal.Open(zap.NewExample(), rc.waldir, walsnap)
+ log.Printf("loading WAL at term %d and index %d", walsnap.GetTerm(), walsnap.GetIndex())
+ w, err := wal.Open(zap.NewExample(), rc.waldir, &walsnap)
if err != nil {
log.Fatalf("raftexample: error loading wal (%v)", err)
}
@@ -254,7 +257,7 @@ func (rc *raftNode) replayWAL() *wal.WAL {
}
rc.raftStorage = raft.NewMemoryStorage()
if snapshot != nil {
- rc.raftStorage.ApplySnapshot(*snapshot)
+ rc.raftStorage.ApplySnapshot(snapshot)
}
rc.raftStorage.SetHardState(st)
@@ -341,7 +344,7 @@ func (rc *raftNode) stopHTTP() {
<-rc.httpdonec
}
-func (rc *raftNode) publishSnapshot(snapshotToSave raftpb.Snapshot) {
+func (rc *raftNode) publishSnapshot(snapshotToSave *raftpb.Snapshot) {
if raft.IsEmptySnap(snapshotToSave) {
return
}
@@ -349,14 +352,14 @@ func (rc *raftNode) publishSnapshot(snapshotToSave raftpb.Snapshot) {
log.Printf("publishing snapshot at index %d", rc.snapshotIndex)
defer log.Printf("finished publishing snapshot at index %d", rc.snapshotIndex)
- if snapshotToSave.Metadata.Index <= rc.appliedIndex {
- log.Fatalf("snapshot index [%d] should > progress.appliedIndex [%d]", snapshotToSave.Metadata.Index, rc.appliedIndex)
+ if snapshotToSave.Metadata.GetIndex() <= rc.appliedIndex {
+ log.Fatalf("snapshot index [%d] should > progress.appliedIndex [%d]", snapshotToSave.Metadata.GetIndex(), rc.appliedIndex)
}
rc.commitC <- nil // trigger kvstore to load snapshot
rc.confState = snapshotToSave.Metadata.ConfState
- rc.snapshotIndex = snapshotToSave.Metadata.Index
- rc.appliedIndex = snapshotToSave.Metadata.Index
+ rc.snapshotIndex = snapshotToSave.Metadata.GetIndex()
+ rc.appliedIndex = snapshotToSave.Metadata.GetIndex()
}
var snapshotCatchUpEntriesN uint64 = 10000
@@ -380,7 +383,7 @@ func (rc *raftNode) maybeTriggerSnapshot(applyDoneC <-chan struct{}) {
if err != nil {
log.Panic(err)
}
- snap, err := rc.raftStorage.CreateSnapshot(rc.appliedIndex, &rc.confState, data)
+ snap, err := rc.raftStorage.CreateSnapshot(rc.appliedIndex, rc.confState, data)
if err != nil {
panic(err)
}
@@ -409,8 +412,8 @@ func (rc *raftNode) serveChannels() {
panic(err)
}
rc.confState = snap.Metadata.ConfState
- rc.snapshotIndex = snap.Metadata.Index
- rc.appliedIndex = snap.Metadata.Index
+ rc.snapshotIndex = snap.Metadata.GetIndex()
+ rc.appliedIndex = snap.Metadata.GetIndex()
defer rc.wal.Close()
@@ -436,7 +439,7 @@ func (rc *raftNode) serveChannels() {
rc.confChangeC = nil
} else {
confChangeCount++
- cc.ID = confChangeCount
+ cc.Id = new(confChangeCount)
rc.node.ProposeConfChange(context.TODO(), cc)
}
}
@@ -458,7 +461,11 @@ func (rc *raftNode) serveChannels() {
if !raft.IsEmptySnap(rd.Snapshot) {
rc.saveSnap(rd.Snapshot)
}
- rc.wal.Save(rd.HardState, rd.Entries)
+ var hs *raftpb.HardState
+ if !raft.IsEmptyHardState(rd.HardState) {
+ hs = rd.HardState
+ }
+ rc.wal.Save(hs, rd.Entries)
if !raft.IsEmptySnap(rd.Snapshot) {
rc.raftStorage.ApplySnapshot(rd.Snapshot)
rc.publishSnapshot(rd.Snapshot)
@@ -487,13 +494,15 @@ func (rc *raftNode) serveChannels() {
// When there is a `raftpb.EntryConfChange` after creating the snapshot,
// then the confState included in the snapshot is out of date. so We need
// to update the confState before sending a snapshot to a follower.
-func (rc *raftNode) processMessages(ms []raftpb.Message) []raftpb.Message {
+func (rc *raftNode) processMessages(ms []*raftpb.Message) []*raftpb.Message {
+ var messages []*raftpb.Message
for i := 0; i < len(ms); i++ {
- if ms[i].Type == raftpb.MsgSnap {
+ if ms[i].GetType() == raftpb.MsgSnap {
ms[i].Snapshot.Metadata.ConfState = rc.confState
}
+ messages = append(messages, ms[i])
}
- return ms
+ return messages
}
func (rc *raftNode) serveRaft() {
@@ -516,7 +525,7 @@ func (rc *raftNode) serveRaft() {
close(rc.httpdonec)
}
-func (rc *raftNode) Process(ctx context.Context, m raftpb.Message) error {
+func (rc *raftNode) Process(ctx context.Context, m *raftpb.Message) error {
return rc.node.Step(ctx, m)
}
func (rc *raftNode) IsIDRemoved(_ uint64) bool { return false }
diff --git a/contrib/raftexample/raft_test.go b/contrib/raftexample/raft_test.go
index a686c8c07071..16c9b019377a 100644
--- a/contrib/raftexample/raft_test.go
+++ b/contrib/raftexample/raft_test.go
@@ -15,10 +15,10 @@
package main
import (
- "reflect"
"testing"
- "github.com/stretchr/testify/require"
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/raft/v3/raftpb"
)
@@ -26,40 +26,40 @@ import (
func TestProcessMessages(t *testing.T) {
cases := []struct {
name string
- confState raftpb.ConfState
- InputMessages []raftpb.Message
- ExpectedMessages []raftpb.Message
+ confState *raftpb.ConfState
+ InputMessages []*raftpb.Message
+ ExpectedMessages []*raftpb.Message
}{
{
name: "only one snapshot message",
- confState: raftpb.ConfState{
+ confState: &raftpb.ConfState{
Voters: []uint64{2, 6, 8, 10},
},
- InputMessages: []raftpb.Message{
+ InputMessages: []*raftpb.Message{
{
- Type: raftpb.MsgSnap,
- To: 8,
+ Type: raftpb.MsgSnap.Enum(),
+ To: new(uint64(8)),
Snapshot: &raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: 100,
- Term: 3,
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(uint64(100)),
+ Term: new(uint64(3)),
+ ConfState: &raftpb.ConfState{
Voters: []uint64{2, 6, 8},
- AutoLeave: true,
+ AutoLeave: new(true),
},
},
},
},
},
- ExpectedMessages: []raftpb.Message{
+ ExpectedMessages: []*raftpb.Message{
{
- Type: raftpb.MsgSnap,
- To: 8,
+ Type: raftpb.MsgSnap.Enum(),
+ To: new(uint64(8)),
Snapshot: &raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: 100,
- Term: 3,
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(uint64(100)),
+ Term: new(uint64(3)),
+ ConfState: &raftpb.ConfState{
Voters: []uint64{2, 6, 8, 10},
},
},
@@ -69,48 +69,48 @@ func TestProcessMessages(t *testing.T) {
},
{
name: "one snapshot message and one other message",
- confState: raftpb.ConfState{
+ confState: &raftpb.ConfState{
Voters: []uint64{2, 7, 8, 12},
},
- InputMessages: []raftpb.Message{
+ InputMessages: []*raftpb.Message{
{
- Type: raftpb.MsgSnap,
- To: 8,
+ Type: raftpb.MsgSnap.Enum(),
+ To: new(uint64(8)),
Snapshot: &raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: 100,
- Term: 3,
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(uint64(100)),
+ Term: new(uint64(3)),
+ ConfState: &raftpb.ConfState{
Voters: []uint64{2, 6, 8},
- AutoLeave: true,
+ AutoLeave: new(true),
},
},
},
},
{
- Type: raftpb.MsgApp,
- From: 6,
- To: 8,
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(6)),
+ To: new(uint64(8)),
},
},
- ExpectedMessages: []raftpb.Message{
+ ExpectedMessages: []*raftpb.Message{
{
- Type: raftpb.MsgSnap,
- To: 8,
+ Type: raftpb.MsgSnap.Enum(),
+ To: new(uint64(8)),
Snapshot: &raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: 100,
- Term: 3,
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(uint64(100)),
+ Term: new(uint64(3)),
+ ConfState: &raftpb.ConfState{
Voters: []uint64{2, 7, 8, 12},
},
},
},
},
{
- Type: raftpb.MsgApp,
- From: 6,
- To: 8,
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(6)),
+ To: new(uint64(8)),
},
},
},
@@ -123,7 +123,9 @@ func TestProcessMessages(t *testing.T) {
}
outputMessages := rn.processMessages(tc.InputMessages)
- require.Truef(t, reflect.DeepEqual(outputMessages, tc.ExpectedMessages), "Unexpected messages, expected: %v, got %v", tc.ExpectedMessages, outputMessages)
+ if diff := cmp.Diff(tc.ExpectedMessages, outputMessages, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected diff (-want +got):\n%s", diff)
+ }
})
}
}
diff --git a/contrib/raftexample/raftexample_test.go b/contrib/raftexample/raftexample_test.go
index cda01204a188..5959c4ef05f5 100644
--- a/contrib/raftexample/raftexample_test.go
+++ b/contrib/raftexample/raftexample_test.go
@@ -43,7 +43,7 @@ type cluster struct {
commitC []<-chan *commit
errorC []<-chan error
proposeC []chan string
- confChangeC []chan raftpb.ConfChange
+ confChangeC []chan *raftpb.ConfChange
snapshotTriggeredC []<-chan struct{}
}
@@ -59,7 +59,7 @@ func newCluster(n int) *cluster {
commitC: make([]<-chan *commit, len(peers)),
errorC: make([]<-chan error, len(peers)),
proposeC: make([]chan string, len(peers)),
- confChangeC: make([]chan raftpb.ConfChange, len(peers)),
+ confChangeC: make([]chan *raftpb.ConfChange, len(peers)),
snapshotTriggeredC: make([]<-chan struct{}, len(peers)),
}
@@ -67,7 +67,7 @@ func newCluster(n int) *cluster {
os.RemoveAll(fmt.Sprintf("raftexample-%d", i+1))
os.RemoveAll(fmt.Sprintf("raftexample-%d-snap", i+1))
clus.proposeC[i] = make(chan string, 1)
- clus.confChangeC[i] = make(chan raftpb.ConfChange, 1)
+ clus.confChangeC[i] = make(chan *raftpb.ConfChange, 1)
fn, snapshotTriggeredC := getSnapshotFn()
clus.snapshotTriggeredC[i] = snapshotTriggeredC
clus.commitC[i], clus.errorC[i], _ = newRaftNode(i+1, clus.peers, false, fn, clus.proposeC[i], clus.confChangeC[i])
@@ -155,14 +155,12 @@ func TestCloseProposerInflight(t *testing.T) {
defer clus.closeNoErrors(t)
var wg sync.WaitGroup
- wg.Add(1)
// some inflight ops
- go func() {
- defer wg.Done()
+ wg.Go(func() {
clus.proposeC[0] <- "foo"
clus.proposeC[0] <- "bar"
- }()
+ })
// wait for one message
if c, ok := <-clus.commitC[0]; !ok || c.data[0] != "foo" {
@@ -178,7 +176,7 @@ func TestPutAndGetKeyValue(t *testing.T) {
proposeC := make(chan string)
defer close(proposeC)
- confChangeC := make(chan raftpb.ConfChange)
+ confChangeC := make(chan *raftpb.ConfChange)
defer close(confChangeC)
var kvs *kvstore
@@ -234,16 +232,16 @@ func TestAddNewNode(t *testing.T) {
}()
newNodeURL := "http://127.0.0.1:10004"
- clus.confChangeC[0] <- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 4,
+ clus.confChangeC[0] <- &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(4)),
Context: []byte(newNodeURL),
}
proposeC := make(chan string)
defer close(proposeC)
- confChangeC := make(chan raftpb.ConfChange)
+ confChangeC := make(chan *raftpb.ConfChange)
defer close(confChangeC)
newRaftNode(4, append(clus.peers, newNodeURL), true, nil, proposeC, confChangeC)
diff --git a/etcdctl/.gomodguard.yaml b/etcdctl/.gomodguard.yaml
new file mode 100644
index 000000000000..18ffc6800c1f
--- /dev/null
+++ b/etcdctl/.gomodguard.yaml
@@ -0,0 +1,9 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/etcdctl/LICENSE b/etcdctl/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/etcdctl/LICENSE
+++ b/etcdctl/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/etcdctl/README.md b/etcdctl/README.md
index 3589f4d699d6..460f3f857e1f 100644
--- a/etcdctl/README.md
+++ b/etcdctl/README.md
@@ -1,3 +1,5 @@
+
+
etcdctl
========
@@ -134,7 +136,9 @@ RPC: Range
- min-mod-revision -- restrict results to kvs with modified revision greater or equal than the supplied revision
#### Output
+
Prints the data in format below,
+
```
\\n\\n\\n\...
```
@@ -143,7 +147,6 @@ Note serializable requests are better for lower latency requirement, but
stale data might be returned if serializable option (`--consistency=s`)
is specified.
-
#### Examples
First, populate etcd with some keys:
@@ -283,6 +286,7 @@ RPC: Txn
- interactive -- input transaction with interactive prompting.
#### Input Format
+
```ebnf
::= * "\n" "\n" "\n"
::= (||||) "\n"
@@ -309,6 +313,7 @@ RPC: Txn
#### Examples
txn in interactive mode:
+
```bash
./etcdctl txn -i
# compares:
@@ -329,6 +334,7 @@ put key2 "some extra key"
```
txn in non-interactive mode:
+
```bash
./etcdctl txn <<<'mod("key1") > "0"
@@ -384,6 +390,7 @@ RPC: Compact
Prints the compacted revision.
#### Example
+
```bash
./etcdctl compaction 1234
# compacted revision 1234
@@ -642,6 +649,7 @@ RPC: LeaseKeepAlive
Prints a message for every keep alive sent or prints a message indicating the lease is gone.
#### Example
+
```bash
./etcdctl lease keep-alive 32695410dcc0ca0
# lease 32695410dcc0ca0 keepalived with TTL(100)
@@ -727,6 +735,7 @@ MEMBER LIST prints the member details for all members associated with an etcd cl
RPC: MemberList
#### Options
+
- consistency -- Linearizable(l) or Serializable(s), defaults to Linearizable(l).
#### Output
@@ -990,7 +999,6 @@ DEFRAG defragments the backend database file for a set of given endpoints while
**Note that defragmentation request does not get replicated over cluster. That is, the request is only applied to the local node. Specify all members in `--endpoints` flag or `--cluster` flag to automatically find all cluster members.**
-
#### Output
For each endpoints, prints a message indicating whether the endpoint was successfully defragmented.
@@ -1031,6 +1039,7 @@ The backend snapshot is written to the given file path.
#### Example
Save a snapshot to "snapshot.db":
+
```
./etcdctl snapshot save snapshot.db
```
@@ -1039,7 +1048,6 @@ Save a snapshot to "snapshot.db":
Removed in v3.6. Use `etcdutl snapshot restore` instead.
-
### SNAPSHOT STATUS \
Removed in v3.6. Use `etcdutl snapshot status` instead.
@@ -1079,6 +1087,7 @@ New members will refuse joining cluster with cluster version newer than theirs,
Downgrade commands allow cluster administrator to force cluster version to be lowered to previous minor version, thus allowing to downgrade the cluster.
Downgrade should be executed in stages:
+
1. Verify that cluster is ready to be downgraded by running `etcdctl downgrade validate `
2. Start the downgrade process by running `etcdctl downgrade enable `
3. For each cluster member:
@@ -1130,6 +1139,7 @@ DOWNGRADE CANCEL cancels the ongoing downgrade action to cluster.
Downgrade cancel success, cluster version 3.5
```
+
## Concurrency commands
### LOCK [options] \ [command arg1 arg2 ...]
@@ -1163,6 +1173,7 @@ Acquire lock and execute `echo lock acquired`:
```
Acquire lock and execute `etcdctl put` command
+
```bash
./etcdctl lock mylock ./etcdctl put foo bar
# OK
@@ -1551,7 +1562,6 @@ The approximate total number of keys transferred to the destination cluster, upd
[mirror]: ./doc/mirror_maker.md
-
### VERSION
Prints the version of etcdctl.
@@ -1578,7 +1588,6 @@ CHECK PERF checks the performance of the etcd cluster for 60 seconds. Running th
Notice that different workload models use different configurations in terms of number of clients and throughput. Here is the configuration for each load:
-
| Load | Number of clients | Number of put requests (requests/sec) |
|---------|------|---------|
| Small | 50 | 10000 |
@@ -1592,10 +1601,8 @@ The test checks for the following conditions:
- All the requests should be done in less than 500 ms
- The standard deviation of the requests should be less than 100 ms
-
Hence, a workload model may work while another one might fail.
-
RPC: CheckPerf
#### Options
@@ -1709,7 +1716,4 @@ backward compatibility for `JSON` format and the format in non-interactive mode.
[etcd]: https://github.com/coreos/etcd
[READMEv2]: READMEv2.md
-[v2key]: ../store/node_extern.go#L28-L37
-[v3key]: ../api/mvccpb/kv.proto#L12-L29
[etcdrpc]: ../api/etcdserverpb/rpc.proto
-[storagerpc]: ../api/mvccpb/kv.proto
diff --git a/etcdctl/ctlv3/command/alarm_command.go b/etcdctl/ctlv3/command/alarm_command.go
index 679f9d98f27c..f0bb9f554c1f 100644
--- a/etcdctl/ctlv3/command/alarm_command.go
+++ b/etcdctl/ctlv3/command/alarm_command.go
@@ -26,8 +26,10 @@ import (
// NewAlarmCommand returns the cobra command for "alarm".
func NewAlarmCommand() *cobra.Command {
ac := &cobra.Command{
- Use: "alarm ",
- Short: "Alarm related commands",
+ Use: "alarm ",
+ Short: "Alarm related commands. Use `etcdctl alarm --help` to see subcommands",
+ Long: "Alarm related commands",
+ GroupID: groupClusterMaintenanceID,
}
ac.AddCommand(NewAlarmDisarmCommand())
@@ -56,7 +58,7 @@ func alarmDisarmCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.Alarm(*resp)
+ display.Alarm(resp)
}
func NewAlarmListCommand() *cobra.Command {
@@ -79,5 +81,5 @@ func alarmListCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.Alarm(*resp)
+ display.Alarm(resp)
}
diff --git a/etcdctl/ctlv3/command/auth_command.go b/etcdctl/ctlv3/command/auth_command.go
index 13ccda44d16c..d120a043f81b 100644
--- a/etcdctl/ctlv3/command/auth_command.go
+++ b/etcdctl/ctlv3/command/auth_command.go
@@ -27,8 +27,10 @@ import (
// NewAuthCommand returns the cobra command for "auth".
func NewAuthCommand() *cobra.Command {
ac := &cobra.Command{
- Use: "auth ",
- Short: "Enable or disable authentication",
+ Use: "auth ",
+ Short: "Enable or disable authentication. Use `etcdctl auth --help` to see subcommands",
+ Long: "Enable or disable authentication",
+ GroupID: groupAuthenticationID,
}
ac.AddCommand(newAuthEnableCommand())
@@ -59,7 +61,7 @@ func authStatusCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.AuthStatus(*result)
+ display.AuthStatus(result)
}
func newAuthEnableCommand() *cobra.Command {
diff --git a/etcdctl/ctlv3/command/check.go b/etcdctl/ctlv3/command/check.go
index 4626d5222f2a..904120982c56 100644
--- a/etcdctl/ctlv3/command/check.go
+++ b/etcdctl/ctlv3/command/check.go
@@ -107,8 +107,10 @@ var checkDatascaleCfgMap = map[string]checkDatascaleCfg{
// NewCheckCommand returns the cobra command for "check".
func NewCheckCommand() *cobra.Command {
cc := &cobra.Command{
- Use: "check ",
- Short: "commands for checking properties of the etcd cluster",
+ Use: "check ",
+ Short: "commands for checking properties of the etcd cluster. Use `etcdctl check --help` to see subcommands",
+ Long: "commands for checking properties of the etcd cluster",
+ GroupID: groupUtilityID,
}
cc.AddCommand(NewCheckPerfCommand())
@@ -182,7 +184,7 @@ func newCheckPerfCommand(cmd *cobra.Command, args []string) {
bar := pb.New(cfg.duration)
bar.Start()
- r := report.NewReport("%4.4f")
+ r := report.NewReport("%4.4f", "", false)
var wg sync.WaitGroup
wg.Add(len(clients))
@@ -353,7 +355,7 @@ func newCheckDatascaleCommand(cmd *cobra.Command, args []string) {
ksize, vsize := 512, 512
k, v := make([]byte, ksize), string(make([]byte, vsize))
- r := report.NewReport("%4.4f")
+ r := report.NewReport("%4.4f", "", false)
var wg sync.WaitGroup
wg.Add(len(clients))
diff --git a/etcdctl/ctlv3/command/compaction_command.go b/etcdctl/ctlv3/command/compaction_command.go
index 5c0bb1019a92..457538a59919 100644
--- a/etcdctl/ctlv3/command/compaction_command.go
+++ b/etcdctl/ctlv3/command/compaction_command.go
@@ -29,9 +29,10 @@ var compactPhysical bool
// NewCompactionCommand returns the cobra command for "compaction".
func NewCompactionCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "compaction [options] ",
- Short: "Compacts the event history in etcd",
- Run: compactionCommandFunc,
+ Use: "compaction [options] ",
+ Short: "Compacts the event history in etcd",
+ Run: compactionCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().BoolVar(&compactPhysical, "physical", false, "'true' to wait for compaction to physically remove all old revisions")
return cmd
diff --git a/etcdctl/ctlv3/command/completion_command.go b/etcdctl/ctlv3/command/completion_command.go
index cb98d4a7d698..45f28442646e 100644
--- a/etcdctl/ctlv3/command/completion_command.go
+++ b/etcdctl/ctlv3/command/completion_command.go
@@ -78,6 +78,7 @@ PowerShell:
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
+ GroupID: groupUtilityID,
}
return cmd
diff --git a/etcdctl/ctlv3/command/defrag_command.go b/etcdctl/ctlv3/command/defrag_command.go
index 36116d4888c7..c68de9cd13d4 100644
--- a/etcdctl/ctlv3/command/defrag_command.go
+++ b/etcdctl/ctlv3/command/defrag_command.go
@@ -27,9 +27,10 @@ import (
// NewDefragCommand returns the cobra command for "Defrag".
func NewDefragCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "defrag",
- Short: "Defragments the storage of the etcd members with given endpoints",
- Run: defragCommandFunc,
+ Use: "defrag",
+ Short: "Defragments the storage of the etcd members with given endpoints",
+ Run: defragCommandFunc,
+ GroupID: groupClusterMaintenanceID,
}
cmd.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
return cmd
diff --git a/etcdctl/ctlv3/command/del_command.go b/etcdctl/ctlv3/command/del_command.go
index 51b7abb3edf8..3b03d7bd8844 100644
--- a/etcdctl/ctlv3/command/del_command.go
+++ b/etcdctl/ctlv3/command/del_command.go
@@ -35,9 +35,10 @@ var (
// NewDelCommand returns the cobra command for "del".
func NewDelCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "del [options] [range_end]",
- Short: "Removes the specified key or range of keys [key, range_end)",
- Run: delCommandFunc,
+ Use: "del [options] [range_end]",
+ Short: "Removes the specified key or range of keys [key, range_end)",
+ Run: delCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().BoolVar(&delPrefix, "prefix", false, "delete keys with matching prefix")
@@ -56,7 +57,7 @@ func delCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.Del(*resp)
+ display.Del(resp)
}
func getDelOp(args []string) (string, []clientv3.OpOption) {
diff --git a/etcdctl/ctlv3/command/downgrade_command.go b/etcdctl/ctlv3/command/downgrade_command.go
index 8b6ab9cd19e3..3db3e836a067 100644
--- a/etcdctl/ctlv3/command/downgrade_command.go
+++ b/etcdctl/ctlv3/command/downgrade_command.go
@@ -26,8 +26,10 @@ import (
// NewDowngradeCommand returns the cobra command for "downgrade".
func NewDowngradeCommand() *cobra.Command {
dc := &cobra.Command{
- Use: "downgrade ",
- Short: "Downgrade related commands",
+ Use: "downgrade ",
+ Short: "Downgrade related commands. Use `etcdctl downgrade --help` to see subcommands",
+ Long: "Downgrade related commands",
+ GroupID: groupClusterMaintenanceID,
}
dc.AddCommand(NewDowngradeValidateCommand())
@@ -93,7 +95,7 @@ func downgradeValidateCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.DowngradeValidate(*resp)
+ display.DowngradeValidate(resp)
}
// downgradeEnableCommandFunc executes the "downgrade enable" command.
@@ -119,7 +121,7 @@ func downgradeEnableCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.DowngradeEnable(*resp)
+ display.DowngradeEnable(resp)
}
// downgradeCancelCommandFunc executes the "downgrade cancel" command.
@@ -133,5 +135,5 @@ func downgradeCancelCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.DowngradeCancel(*resp)
+ display.DowngradeCancel(resp)
}
diff --git a/etcdctl/ctlv3/command/elect_command.go b/etcdctl/ctlv3/command/elect_command.go
index eee4289e710e..8c171873e485 100644
--- a/etcdctl/ctlv3/command/elect_command.go
+++ b/etcdctl/ctlv3/command/elect_command.go
@@ -33,9 +33,10 @@ var electListen bool
// NewElectCommand returns the cobra command for "elect".
func NewElectCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "elect [proposal]",
- Short: "Observes and participates in leader election",
- Run: electCommandFunc,
+ Use: "elect [proposal]",
+ Short: "Observes and participates in leader election",
+ Run: electCommandFunc,
+ GroupID: groupConcurrencyID,
}
cmd.Flags().BoolVarP(&electListen, "listen", "l", false, "observation mode")
return cmd
@@ -124,7 +125,7 @@ func campaign(c *clientv3.Client, election string, prop string) error {
if err != nil {
return err
}
- display.Get(*resp)
+ display.Get(resp)
select {
case <-donec:
diff --git a/etcdctl/ctlv3/command/ep_command.go b/etcdctl/ctlv3/command/ep_command.go
index 52518f192651..92019b064cb8 100644
--- a/etcdctl/ctlv3/command/ep_command.go
+++ b/etcdctl/ctlv3/command/ep_command.go
@@ -39,8 +39,10 @@ var (
// NewEndpointCommand returns the cobra command for "endpoint".
func NewEndpointCommand() *cobra.Command {
ec := &cobra.Command{
- Use: "endpoint ",
- Short: "Endpoint related commands",
+ Use: "endpoint ",
+ Short: "Endpoint related commands. Use `etcdctl endpoint --help` to see subcommands",
+ Long: "Endpoint related commands",
+ GroupID: groupClusterMaintenanceID,
}
ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list")
diff --git a/etcdctl/ctlv3/command/get_command.go b/etcdctl/ctlv3/command/get_command.go
index 7d687aa5c90c..59e70139edb5 100644
--- a/etcdctl/ctlv3/command/get_command.go
+++ b/etcdctl/ctlv3/command/get_command.go
@@ -39,14 +39,16 @@ var (
getMaxCreateRev int64
getMinModRev int64
getMaxModRev int64
+ getStream bool
)
// NewGetCommand returns the cobra command for "get".
func NewGetCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "get [options] [range_end]",
- Short: "Gets the key or a range of keys",
- Run: getCommandFunc,
+ Use: "get [options] [range_end]",
+ Short: "Gets the key or a range of keys",
+ Run: getCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().StringVar(&getConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
@@ -63,6 +65,7 @@ func NewGetCommand() *cobra.Command {
cmd.Flags().Int64Var(&getMaxCreateRev, "max-create-rev", 0, "Maximum create revision")
cmd.Flags().Int64Var(&getMinModRev, "min-mod-rev", 0, "Minimum modification revision")
cmd.Flags().Int64Var(&getMaxModRev, "max-mod-rev", 0, "Maximum modification revision")
+ cmd.Flags().BoolVar(&getStream, "stream", false, "Use the RangeStream RPC")
cmd.RegisterFlagCompletionFunc("consistency", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"l", "s"}, cobra.ShellCompDirectiveDefault
@@ -81,7 +84,20 @@ func NewGetCommand() *cobra.Command {
func getCommandFunc(cmd *cobra.Command, args []string) {
key, opts := getGetOp(args)
ctx, cancel := commandCtx(cmd)
- resp, err := mustClientFromCmd(cmd).Get(ctx, key, opts...)
+ client := mustClientFromCmd(cmd)
+ var (
+ resp *clientv3.GetResponse
+ err error
+ )
+ if getStream {
+ var stream clientv3.GetStreamChan
+ stream, err = client.GetStream(ctx, key, opts...)
+ if err == nil {
+ resp, err = clientv3.GetStreamToGetResponse(stream)
+ }
+ } else {
+ resp, err = client.Get(ctx, key, opts...)
+ }
cancel()
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
@@ -100,7 +116,7 @@ func getCommandFunc(cmd *cobra.Command, args []string) {
}
dp.valueOnly = true
}
- display.Get(*resp)
+ display.Get(resp)
}
func getGetOp(args []string) (string, []clientv3.OpOption) {
diff --git a/etcdctl/ctlv3/command/global.go b/etcdctl/ctlv3/command/global.go
index 019b638d3940..602f2776d2d0 100644
--- a/etcdctl/ctlv3/command/global.go
+++ b/etcdctl/ctlv3/command/global.go
@@ -58,6 +58,7 @@ type GlobalFlags struct {
User string
Password string
+ Token string
Debug bool
}
@@ -290,13 +291,22 @@ func authCfgFromCmd(cmd *cobra.Command) *clientv3.AuthConfig {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
+ tokenFlag, err := cmd.Flags().GetString("auth-jwt-token")
+ if err != nil {
+ cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
+ }
- if userFlag == "" {
+ if userFlag == "" && tokenFlag == "" {
return nil
}
var cfg clientv3.AuthConfig
+ if tokenFlag != "" {
+ cfg.Token = tokenFlag
+ return &cfg
+ }
+
if passwordFlag == "" {
splitted := strings.SplitN(userFlag, ":", 2)
if len(splitted) < 2 {
diff --git a/etcdctl/ctlv3/command/groups.go b/etcdctl/ctlv3/command/groups.go
new file mode 100644
index 000000000000..ca0229d3c5d2
--- /dev/null
+++ b/etcdctl/ctlv3/command/groups.go
@@ -0,0 +1,60 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 command
+
+import "github.com/spf13/cobra"
+
+const (
+ groupKVID = "kv"
+ groupClusterMaintenanceID = "cluster maintenance"
+ groupConcurrencyID = "concurrency"
+ groupAuthenticationID = "authentication"
+ groupUtilityID = "utility"
+)
+
+func NewKVGroup() *cobra.Group {
+ return &cobra.Group{
+ ID: groupKVID,
+ Title: "Key-value commands",
+ }
+}
+
+func NewClusterMaintenanceGroup() *cobra.Group {
+ return &cobra.Group{
+ ID: groupClusterMaintenanceID,
+ Title: "Cluster maintenance commands",
+ }
+}
+
+func NewConcurrencyGroup() *cobra.Group {
+ return &cobra.Group{
+ ID: groupConcurrencyID,
+ Title: "Concurrency commands",
+ }
+}
+
+func NewAuthenticationGroup() *cobra.Group {
+ return &cobra.Group{
+ ID: groupAuthenticationID,
+ Title: "Authentication commands",
+ }
+}
+
+func NewUtilityGroup() *cobra.Group {
+ return &cobra.Group{
+ ID: groupUtilityID,
+ Title: "Utility commands",
+ }
+}
diff --git a/server/etcdserver/v2_server.go b/etcdctl/ctlv3/command/help_command.go
similarity index 72%
rename from server/etcdserver/v2_server.go
rename to etcdctl/ctlv3/command/help_command.go
index 8636204b544c..2590697910eb 100644
--- a/server/etcdserver/v2_server.go
+++ b/etcdctl/ctlv3/command/help_command.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The etcd Authors
+// Copyright 2025 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,15 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package etcdserver
+package command
-import (
- pb "go.etcd.io/etcd/api/v3/etcdserverpb"
-)
+import "github.com/spf13/cobra"
-type RequestV2 pb.Request
-
-func (r *RequestV2) String() string {
- rpb := pb.Request(*r)
- return rpb.String()
+func SetHelpCmdGroup(rootCmd *cobra.Command) {
+ rootCmd.SetHelpCommandGroupID(groupUtilityID)
}
diff --git a/etcdctl/ctlv3/command/lease_command.go b/etcdctl/ctlv3/command/lease_command.go
index 8e70670b6380..acc82f22acae 100644
--- a/etcdctl/ctlv3/command/lease_command.go
+++ b/etcdctl/ctlv3/command/lease_command.go
@@ -28,8 +28,10 @@ import (
// NewLeaseCommand returns the cobra command for "lease".
func NewLeaseCommand() *cobra.Command {
lc := &cobra.Command{
- Use: "lease ",
- Short: "Lease related commands",
+ Use: "lease ",
+ Short: "Lease related commands. Use `etcdctl lease --help` to see subcommands",
+ Long: "Lease related commands",
+ GroupID: groupKVID,
}
lc.AddCommand(NewLeaseGrantCommand())
@@ -70,7 +72,7 @@ func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, fmt.Errorf("failed to grant lease (%w)", err))
}
- display.Grant(*resp)
+ display.Grant(resp)
}
// NewLeaseRevokeCommand returns the cobra command for "lease revoke".
@@ -98,7 +100,7 @@ func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, fmt.Errorf("failed to revoke lease (%w)", err))
}
- display.Revoke(id, *resp)
+ display.Revoke(id, resp)
}
var timeToLiveKeys bool
@@ -129,7 +131,7 @@ func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
if rerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, rerr)
}
- display.TimeToLive(*resp, timeToLiveKeys)
+ display.TimeToLive(resp, timeToLiveKeys)
}
// NewLeaseListCommand returns the cobra command for "lease list".
@@ -148,7 +150,7 @@ func leaseListCommandFunc(cmd *cobra.Command, args []string) {
if rerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, rerr)
}
- display.Leases(*resp)
+ display.Leases(resp)
}
var leaseKeepAliveOnce bool
@@ -180,7 +182,7 @@ func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
if kerr != nil {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, kerr)
}
- display.KeepAlive(*respc)
+ display.KeepAlive(respc)
return
}
@@ -189,7 +191,7 @@ func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitBadConnection, kerr)
}
for resp := range respc {
- display.KeepAlive(*resp)
+ display.KeepAlive(resp)
}
if _, ok := (display).(*simplePrinter); ok {
diff --git a/etcdctl/ctlv3/command/lock_command.go b/etcdctl/ctlv3/command/lock_command.go
index 64fb0ee8192d..63641eba05d3 100644
--- a/etcdctl/ctlv3/command/lock_command.go
+++ b/etcdctl/ctlv3/command/lock_command.go
@@ -35,9 +35,10 @@ var lockTTL = 10
// NewLockCommand returns the cobra command for "lock".
func NewLockCommand() *cobra.Command {
c := &cobra.Command{
- Use: "lock [exec-command arg1 arg2 ...]",
- Short: "Acquires a named lock",
- Run: lockCommandFunc,
+ Use: "lock [exec-command arg1 arg2 ...]",
+ Short: "Acquires a named lock",
+ Run: lockCommandFunc,
+ GroupID: groupConcurrencyID,
}
c.Flags().IntVarP(&lockTTL, "ttl", "", lockTTL, "timeout for session")
return c
@@ -111,7 +112,7 @@ func lockUntilSignal(c *clientv3.Client, lockname string, cmdArgs []string) erro
if len(k.Kvs) == 0 {
return errors.New("lock lost on init")
}
- display.Get(*k)
+ display.Get(k)
select {
case <-donec:
diff --git a/etcdctl/ctlv3/command/make_mirror_command.go b/etcdctl/ctlv3/command/make_mirror_command.go
index c8b6220d596f..697c9b9d8c63 100644
--- a/etcdctl/ctlv3/command/make_mirror_command.go
+++ b/etcdctl/ctlv3/command/make_mirror_command.go
@@ -53,9 +53,10 @@ var (
// NewMakeMirrorCommand returns the cobra command for "makeMirror".
func NewMakeMirrorCommand() *cobra.Command {
c := &cobra.Command{
- Use: "make-mirror [options] ",
- Short: "Makes a mirror at the destination etcd cluster",
- Run: makeMirrorCommandFunc,
+ Use: "make-mirror [options] ",
+ Short: "Makes a mirror at the destination etcd cluster",
+ Run: makeMirrorCommandFunc,
+ GroupID: groupUtilityID,
}
c.Flags().StringVar(&mmprefix, "prefix", "", "Key-value prefix to mirror")
@@ -216,10 +217,10 @@ func makeMirror(ctx context.Context, c *clientv3.Client, dc *clientv3.Client) er
}
switch ev.Type {
- case mvccpb.PUT:
+ case mvccpb.Event_PUT:
ops = append(ops, clientv3.OpPut(modifyPrefix(string(ev.Kv.Key)), string(ev.Kv.Value)))
atomic.AddInt64(&total, 1)
- case mvccpb.DELETE:
+ case mvccpb.Event_DELETE:
ops = append(ops, clientv3.OpDelete(modifyPrefix(string(ev.Kv.Key))))
atomic.AddInt64(&total, 1)
default:
diff --git a/etcdctl/ctlv3/command/member_command.go b/etcdctl/ctlv3/command/member_command.go
index 256bf2b9c7b4..871c5822a17a 100644
--- a/etcdctl/ctlv3/command/member_command.go
+++ b/etcdctl/ctlv3/command/member_command.go
@@ -35,8 +35,10 @@ var (
// NewMemberCommand returns the cobra command for "member".
func NewMemberCommand() *cobra.Command {
mc := &cobra.Command{
- Use: "member ",
- Short: "Membership related commands",
+ Use: "member ",
+ Short: "Membership related commands. Use `etcdctl member --help` to see subcommands",
+ Long: "Membership related commands",
+ GroupID: groupClusterMaintenanceID,
}
mc.AddCommand(NewMemberAddCommand())
@@ -158,7 +160,7 @@ func memberAddCommandFunc(cmd *cobra.Command, args []string) {
}
newID := resp.Member.ID
- display.MemberAdd(*resp)
+ display.MemberAdd(resp)
if _, ok := (display).(*simplePrinter); ok {
var conf []string
@@ -197,7 +199,7 @@ func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.MemberRemove(id, *resp)
+ display.MemberRemove(id, resp)
}
// memberUpdateCommandFunc executes the "member update" command.
@@ -224,7 +226,7 @@ func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.MemberUpdate(id, *resp)
+ display.MemberUpdate(id, resp)
}
// memberListCommandFunc executes the "member list" command.
@@ -240,7 +242,7 @@ func memberListCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.MemberList(*resp)
+ display.MemberList(resp)
}
// memberPromoteCommandFunc executes the "member promote" command.
@@ -260,5 +262,5 @@ func memberPromoteCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.MemberPromote(id, *resp)
+ display.MemberPromote(id, resp)
}
diff --git a/etcdctl/ctlv3/command/move_leader_command.go b/etcdctl/ctlv3/command/move_leader_command.go
index a7b4f397b1c8..4b74cf23969e 100644
--- a/etcdctl/ctlv3/command/move_leader_command.go
+++ b/etcdctl/ctlv3/command/move_leader_command.go
@@ -27,9 +27,10 @@ import (
// NewMoveLeaderCommand returns the cobra command for "move-leader".
func NewMoveLeaderCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "move-leader ",
- Short: "Transfers leadership to another etcd cluster member.",
- Run: transferLeadershipCommandFunc,
+ Use: "move-leader ",
+ Short: "Transfers leadership to another etcd cluster member.",
+ Run: transferLeadershipCommandFunc,
+ GroupID: groupClusterMaintenanceID,
}
return cmd
}
@@ -80,5 +81,5 @@ func transferLeadershipCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.MoveLeader(leaderID, target, *resp)
+ display.MoveLeader(leaderID, target, resp)
}
diff --git a/etcdctl/ctlv3/command/options_command.go b/etcdctl/ctlv3/command/options_command.go
new file mode 100644
index 000000000000..b48185f10366
--- /dev/null
+++ b/etcdctl/ctlv3/command/options_command.go
@@ -0,0 +1,46 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 command
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+func NewOptionsCommand(rootCmd *cobra.Command) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "options",
+ Short: "Show the global command-line flags",
+ Run: func(cmd *cobra.Command, args []string) {
+ fs := unhideCopy(rootCmd.PersistentFlags())
+ fmt.Fprintf(cmd.OutOrStdout(), "The following options can be passed to any command:\n\n")
+ fmt.Fprint(cmd.OutOrStdout(), fs.FlagUsages())
+ },
+ GroupID: groupUtilityID,
+ }
+ return cmd
+}
+
+func unhideCopy(src *pflag.FlagSet) *pflag.FlagSet {
+ out := pflag.NewFlagSet("global", pflag.ContinueOnError)
+ src.VisitAll(func(f *pflag.Flag) {
+ nf := *f
+ nf.Hidden = false
+ out.AddFlag(&nf)
+ })
+ return out
+}
diff --git a/etcdctl/ctlv3/command/printer.go b/etcdctl/ctlv3/command/printer.go
index 12460259eb33..f3514df658ed 100644
--- a/etcdctl/ctlv3/command/printer.go
+++ b/etcdctl/ctlv3/command/printer.go
@@ -28,51 +28,51 @@ import (
)
type printer interface {
- Del(v3.DeleteResponse)
- Get(v3.GetResponse)
- Put(v3.PutResponse)
- Txn(v3.TxnResponse)
- Watch(v3.WatchResponse)
-
- Grant(r v3.LeaseGrantResponse)
- Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse)
- KeepAlive(r v3.LeaseKeepAliveResponse)
- TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
- Leases(r v3.LeaseLeasesResponse)
-
- MemberAdd(v3.MemberAddResponse)
- MemberRemove(id uint64, r v3.MemberRemoveResponse)
- MemberUpdate(id uint64, r v3.MemberUpdateResponse)
- MemberPromote(id uint64, r v3.MemberPromoteResponse)
- MemberList(v3.MemberListResponse)
+ Del(*v3.DeleteResponse)
+ Get(*v3.GetResponse)
+ Put(*v3.PutResponse)
+ Txn(*v3.TxnResponse)
+ Watch(*v3.WatchResponse)
+
+ Grant(r *v3.LeaseGrantResponse)
+ Revoke(id v3.LeaseID, r *v3.LeaseRevokeResponse)
+ KeepAlive(r *v3.LeaseKeepAliveResponse)
+ TimeToLive(r *v3.LeaseTimeToLiveResponse, keys bool)
+ Leases(r *v3.LeaseLeasesResponse)
+
+ MemberAdd(*v3.MemberAddResponse)
+ MemberRemove(id uint64, r *v3.MemberRemoveResponse)
+ MemberUpdate(id uint64, r *v3.MemberUpdateResponse)
+ MemberPromote(id uint64, r *v3.MemberPromoteResponse)
+ MemberList(*v3.MemberListResponse)
EndpointHealth([]epHealth)
EndpointStatus([]epStatus)
EndpointHashKV([]epHashKV)
- MoveLeader(leader, target uint64, r v3.MoveLeaderResponse)
-
- DowngradeValidate(r v3.DowngradeResponse)
- DowngradeEnable(r v3.DowngradeResponse)
- DowngradeCancel(r v3.DowngradeResponse)
-
- Alarm(v3.AlarmResponse)
-
- RoleAdd(role string, r v3.AuthRoleAddResponse)
- RoleGet(role string, r v3.AuthRoleGetResponse)
- RoleDelete(role string, r v3.AuthRoleDeleteResponse)
- RoleList(v3.AuthRoleListResponse)
- RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse)
- RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse)
-
- UserAdd(user string, r v3.AuthUserAddResponse)
- UserGet(user string, r v3.AuthUserGetResponse)
- UserList(r v3.AuthUserListResponse)
- UserChangePassword(v3.AuthUserChangePasswordResponse)
- UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse)
- UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse)
- UserDelete(user string, r v3.AuthUserDeleteResponse)
-
- AuthStatus(r v3.AuthStatusResponse)
+ MoveLeader(leader, target uint64, r *v3.MoveLeaderResponse)
+
+ DowngradeValidate(r *v3.DowngradeResponse)
+ DowngradeEnable(r *v3.DowngradeResponse)
+ DowngradeCancel(r *v3.DowngradeResponse)
+
+ Alarm(*v3.AlarmResponse)
+
+ RoleAdd(role string, r *v3.AuthRoleAddResponse)
+ RoleGet(role string, r *v3.AuthRoleGetResponse)
+ RoleDelete(role string, r *v3.AuthRoleDeleteResponse)
+ RoleList(*v3.AuthRoleListResponse)
+ RoleGrantPermission(role string, r *v3.AuthRoleGrantPermissionResponse)
+ RoleRevokePermission(role string, key string, end string, r *v3.AuthRoleRevokePermissionResponse)
+
+ UserAdd(user string, r *v3.AuthUserAddResponse)
+ UserGet(user string, r *v3.AuthUserGetResponse)
+ UserList(r *v3.AuthUserListResponse)
+ UserChangePassword(*v3.AuthUserChangePasswordResponse)
+ UserGrantRole(user string, role string, r *v3.AuthUserGrantRoleResponse)
+ UserRevokeRole(user string, role string, r *v3.AuthUserRevokeRoleResponse)
+ UserDelete(user string, r *v3.AuthUserDeleteResponse)
+
+ AuthStatus(r *v3.AuthStatusResponse)
}
func NewPrinter(printerType string, isHex bool) printer {
@@ -96,72 +96,72 @@ type printerRPC struct {
p func(any)
}
-func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) }
-func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) }
-func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) }
-func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) }
-func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) }
-
-func (p *printerRPC) Grant(r v3.LeaseGrantResponse) { p.p(r) }
-func (p *printerRPC) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) { p.p(r) }
-func (p *printerRPC) KeepAlive(r v3.LeaseKeepAliveResponse) { p.p(r) }
-func (p *printerRPC) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { p.p(&r) }
-func (p *printerRPC) Leases(r v3.LeaseLeasesResponse) { p.p(&r) }
-
-func (p *printerRPC) MemberAdd(r v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(&r)) }
-func (p *printerRPC) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
- p.p((*pb.MemberRemoveResponse)(&r))
+func (p *printerRPC) Del(r *v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(r)) }
+func (p *printerRPC) Get(r *v3.GetResponse) { p.p((*pb.RangeResponse)(r)) }
+func (p *printerRPC) Put(r *v3.PutResponse) { p.p((*pb.PutResponse)(r)) }
+func (p *printerRPC) Txn(r *v3.TxnResponse) { p.p((*pb.TxnResponse)(r)) }
+func (p *printerRPC) Watch(r *v3.WatchResponse) { p.p(r) }
+
+func (p *printerRPC) Grant(r *v3.LeaseGrantResponse) { p.p(r) }
+func (p *printerRPC) Revoke(id v3.LeaseID, r *v3.LeaseRevokeResponse) { p.p(r) }
+func (p *printerRPC) KeepAlive(r *v3.LeaseKeepAliveResponse) { p.p(r) }
+func (p *printerRPC) TimeToLive(r *v3.LeaseTimeToLiveResponse, keys bool) { p.p(r) }
+func (p *printerRPC) Leases(r *v3.LeaseLeasesResponse) { p.p(r) }
+
+func (p *printerRPC) MemberAdd(r *v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(r)) }
+func (p *printerRPC) MemberRemove(id uint64, r *v3.MemberRemoveResponse) {
+ p.p((*pb.MemberRemoveResponse)(r))
}
-func (p *printerRPC) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
- p.p((*pb.MemberUpdateResponse)(&r))
+func (p *printerRPC) MemberUpdate(id uint64, r *v3.MemberUpdateResponse) {
+ p.p((*pb.MemberUpdateResponse)(r))
}
-func (p *printerRPC) MemberPromote(id uint64, r v3.MemberPromoteResponse) {
- p.p((*pb.MemberPromoteResponse)(&r))
+func (p *printerRPC) MemberPromote(id uint64, r *v3.MemberPromoteResponse) {
+ p.p((*pb.MemberPromoteResponse)(r))
}
-func (p *printerRPC) MemberList(r v3.MemberListResponse) { p.p((*pb.MemberListResponse)(&r)) }
-func (p *printerRPC) Alarm(r v3.AlarmResponse) { p.p((*pb.AlarmResponse)(&r)) }
-func (p *printerRPC) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) {
- p.p((*pb.MoveLeaderResponse)(&r))
+func (p *printerRPC) MemberList(r *v3.MemberListResponse) { p.p((*pb.MemberListResponse)(r)) }
+func (p *printerRPC) Alarm(r *v3.AlarmResponse) { p.p((*pb.AlarmResponse)(r)) }
+func (p *printerRPC) MoveLeader(leader, target uint64, r *v3.MoveLeaderResponse) {
+ p.p((*pb.MoveLeaderResponse)(r))
}
-func (p *printerRPC) DowngradeValidate(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
-func (p *printerRPC) DowngradeEnable(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
-func (p *printerRPC) DowngradeCancel(r v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(&r)) }
-func (p *printerRPC) RoleAdd(_ string, r v3.AuthRoleAddResponse) { p.p((*pb.AuthRoleAddResponse)(&r)) }
-func (p *printerRPC) RoleGet(_ string, r v3.AuthRoleGetResponse) { p.p((*pb.AuthRoleGetResponse)(&r)) }
-func (p *printerRPC) RoleDelete(_ string, r v3.AuthRoleDeleteResponse) {
- p.p((*pb.AuthRoleDeleteResponse)(&r))
+func (p *printerRPC) DowngradeValidate(r *v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(r)) }
+func (p *printerRPC) DowngradeEnable(r *v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(r)) }
+func (p *printerRPC) DowngradeCancel(r *v3.DowngradeResponse) { p.p((*pb.DowngradeResponse)(r)) }
+func (p *printerRPC) RoleAdd(_ string, r *v3.AuthRoleAddResponse) { p.p((*pb.AuthRoleAddResponse)(r)) }
+func (p *printerRPC) RoleGet(_ string, r *v3.AuthRoleGetResponse) { p.p((*pb.AuthRoleGetResponse)(r)) }
+func (p *printerRPC) RoleDelete(_ string, r *v3.AuthRoleDeleteResponse) {
+ p.p((*pb.AuthRoleDeleteResponse)(r))
}
-func (p *printerRPC) RoleList(r v3.AuthRoleListResponse) { p.p((*pb.AuthRoleListResponse)(&r)) }
-func (p *printerRPC) RoleGrantPermission(_ string, r v3.AuthRoleGrantPermissionResponse) {
- p.p((*pb.AuthRoleGrantPermissionResponse)(&r))
+func (p *printerRPC) RoleList(r *v3.AuthRoleListResponse) { p.p((*pb.AuthRoleListResponse)(r)) }
+func (p *printerRPC) RoleGrantPermission(_ string, r *v3.AuthRoleGrantPermissionResponse) {
+ p.p((*pb.AuthRoleGrantPermissionResponse)(r))
}
-func (p *printerRPC) RoleRevokePermission(_ string, _ string, _ string, r v3.AuthRoleRevokePermissionResponse) {
- p.p((*pb.AuthRoleRevokePermissionResponse)(&r))
+func (p *printerRPC) RoleRevokePermission(_ string, _ string, _ string, r *v3.AuthRoleRevokePermissionResponse) {
+ p.p((*pb.AuthRoleRevokePermissionResponse)(r))
}
-func (p *printerRPC) UserAdd(_ string, r v3.AuthUserAddResponse) { p.p((*pb.AuthUserAddResponse)(&r)) }
-func (p *printerRPC) UserGet(_ string, r v3.AuthUserGetResponse) { p.p((*pb.AuthUserGetResponse)(&r)) }
-func (p *printerRPC) UserList(r v3.AuthUserListResponse) { p.p((*pb.AuthUserListResponse)(&r)) }
-func (p *printerRPC) UserChangePassword(r v3.AuthUserChangePasswordResponse) {
- p.p((*pb.AuthUserChangePasswordResponse)(&r))
+func (p *printerRPC) UserAdd(_ string, r *v3.AuthUserAddResponse) { p.p((*pb.AuthUserAddResponse)(r)) }
+func (p *printerRPC) UserGet(_ string, r *v3.AuthUserGetResponse) { p.p((*pb.AuthUserGetResponse)(r)) }
+func (p *printerRPC) UserList(r *v3.AuthUserListResponse) { p.p((*pb.AuthUserListResponse)(r)) }
+func (p *printerRPC) UserChangePassword(r *v3.AuthUserChangePasswordResponse) {
+ p.p((*pb.AuthUserChangePasswordResponse)(r))
}
-func (p *printerRPC) UserGrantRole(_ string, _ string, r v3.AuthUserGrantRoleResponse) {
- p.p((*pb.AuthUserGrantRoleResponse)(&r))
+func (p *printerRPC) UserGrantRole(_ string, _ string, r *v3.AuthUserGrantRoleResponse) {
+ p.p((*pb.AuthUserGrantRoleResponse)(r))
}
-func (p *printerRPC) UserRevokeRole(_ string, _ string, r v3.AuthUserRevokeRoleResponse) {
- p.p((*pb.AuthUserRevokeRoleResponse)(&r))
+func (p *printerRPC) UserRevokeRole(_ string, _ string, r *v3.AuthUserRevokeRoleResponse) {
+ p.p((*pb.AuthUserRevokeRoleResponse)(r))
}
-func (p *printerRPC) UserDelete(_ string, r v3.AuthUserDeleteResponse) {
- p.p((*pb.AuthUserDeleteResponse)(&r))
+func (p *printerRPC) UserDelete(_ string, r *v3.AuthUserDeleteResponse) {
+ p.p((*pb.AuthUserDeleteResponse)(r))
}
-func (p *printerRPC) AuthStatus(r v3.AuthStatusResponse) {
- p.p((*pb.AuthStatusResponse)(&r))
+func (p *printerRPC) AuthStatus(r *v3.AuthStatusResponse) {
+ p.p((*pb.AuthStatusResponse)(r))
}
type printerUnsupported struct{ printerRPC }
@@ -177,13 +177,16 @@ func (p *printerUnsupported) EndpointHealth([]epHealth) { p.p(nil) }
func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) }
-func (p *printerUnsupported) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) { p.p(nil) }
-func (p *printerUnsupported) DowngradeValidate(r v3.DowngradeResponse) { p.p(nil) }
-func (p *printerUnsupported) DowngradeEnable(r v3.DowngradeResponse) { p.p(nil) }
-func (p *printerUnsupported) DowngradeCancel(r v3.DowngradeResponse) { p.p(nil) }
+func (p *printerUnsupported) MoveLeader(leader, target uint64, r *v3.MoveLeaderResponse) { p.p(nil) }
+func (p *printerUnsupported) DowngradeValidate(r *v3.DowngradeResponse) { p.p(nil) }
+func (p *printerUnsupported) DowngradeEnable(r *v3.DowngradeResponse) { p.p(nil) }
+func (p *printerUnsupported) DowngradeCancel(r *v3.DowngradeResponse) { p.p(nil) }
-func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string) {
+func makeMemberListTable(r *v3.MemberListResponse) (hdr []string, rows [][]string) {
hdr = []string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs", "Is Learner"}
+ if r == nil {
+ return hdr, rows
+ }
for _, m := range r.Members {
status := "started"
if len(m.Name) == 0 {
@@ -224,23 +227,24 @@ func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]stri
"raft index", "raft applied index", "errors", "downgrade target version", "downgrade enabled",
}
for _, status := range statusList {
+ resp := (*pb.StatusResponse)(status.Resp)
rows = append(rows, []string{
status.Ep,
- fmt.Sprintf("%x", status.Resp.Header.MemberId),
- status.Resp.Version,
- status.Resp.StorageVersion,
- humanize.Bytes(uint64(status.Resp.DbSize)),
- humanize.Bytes(uint64(status.Resp.DbSizeInUse)),
- fmt.Sprintf("%d%%", int(float64(100-(status.Resp.DbSizeInUse*100/status.Resp.DbSize)))),
- humanize.Bytes(uint64(status.Resp.DbSizeQuota)),
- fmt.Sprint(status.Resp.Leader == status.Resp.Header.MemberId),
- fmt.Sprint(status.Resp.IsLearner),
- fmt.Sprint(status.Resp.RaftTerm),
- fmt.Sprint(status.Resp.RaftIndex),
- fmt.Sprint(status.Resp.RaftAppliedIndex),
- fmt.Sprint(strings.Join(status.Resp.Errors, ", ")),
- status.Resp.DowngradeInfo.GetTargetVersion(),
- strconv.FormatBool(status.Resp.DowngradeInfo.GetEnabled()),
+ fmt.Sprintf("%x", resp.GetHeader().GetMemberId()),
+ resp.GetVersion(),
+ resp.GetStorageVersion(),
+ humanize.Bytes(uint64(resp.GetDbSize())),
+ humanize.Bytes(uint64(resp.GetDbSizeInUse())),
+ fmt.Sprintf("%d%%", int(float64(100-(resp.GetDbSizeInUse()*100/resp.GetDbSize())))),
+ humanize.Bytes(uint64(resp.GetDbSizeQuota())),
+ fmt.Sprint(resp.GetLeader() == resp.GetHeader().GetMemberId()),
+ fmt.Sprint(resp.GetIsLearner()),
+ fmt.Sprint(resp.GetRaftTerm()),
+ fmt.Sprint(resp.GetRaftIndex()),
+ fmt.Sprint(resp.GetRaftAppliedIndex()),
+ fmt.Sprint(strings.Join(resp.GetErrors(), ", ")),
+ resp.GetDowngradeInfo().GetTargetVersion(),
+ strconv.FormatBool(resp.GetDowngradeInfo().GetEnabled()),
})
}
return hdr, rows
@@ -249,10 +253,11 @@ func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]stri
func makeEndpointHashKVTable(hashList []epHashKV) (hdr []string, rows [][]string) {
hdr = []string{"endpoint", "hash", "hash_revision"}
for _, h := range hashList {
+ resp := (*pb.HashKVResponse)(h.Resp)
rows = append(rows, []string{
h.Ep,
- fmt.Sprint(h.Resp.Hash),
- fmt.Sprint(h.Resp.HashRevision),
+ fmt.Sprint(resp.GetHash()),
+ fmt.Sprint(resp.GetHashRevision()),
})
}
return hdr, rows
diff --git a/etcdctl/ctlv3/command/printer_fields.go b/etcdctl/ctlv3/command/printer_fields.go
index 3e97fe4dae70..5feafe70e7a6 100644
--- a/etcdctl/ctlv3/command/printer_fields.go
+++ b/etcdctl/ctlv3/command/printer_fields.go
@@ -29,88 +29,96 @@ type fieldsPrinter struct {
}
func (p *fieldsPrinter) kv(pfx string, kv *spb.KeyValue) {
- fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.Key))
- fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.CreateRevision)
- fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.ModRevision)
- fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.Version)
- fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.Value))
+ fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.GetKey()))
+ fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.GetCreateRevision())
+ fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.GetModRevision())
+ fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.GetVersion())
+ fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.GetValue()))
if p.isHex {
- fmt.Printf("\"%sLease\" : %016x\n", pfx, kv.Lease)
+ fmt.Printf("\"%sLease\" : %016x\n", pfx, kv.GetLease())
} else {
- fmt.Printf("\"%sLease\" : %d\n", pfx, kv.Lease)
+ fmt.Printf("\"%sLease\" : %d\n", pfx, kv.GetLease())
}
}
func (p *fieldsPrinter) hdr(h *pb.ResponseHeader) {
if p.isHex {
- fmt.Println(`"ClusterID" :`, types.ID(h.ClusterId))
- fmt.Println(`"MemberID" :`, types.ID(h.MemberId))
+ fmt.Println(`"ClusterID" :`, types.ID(h.GetClusterId()))
+ fmt.Println(`"MemberID" :`, types.ID(h.GetMemberId()))
} else {
- fmt.Println(`"ClusterID" :`, h.ClusterId)
- fmt.Println(`"MemberID" :`, h.MemberId)
+ fmt.Println(`"ClusterID" :`, h.GetClusterId())
+ fmt.Println(`"MemberID" :`, h.GetMemberId())
}
// Revision only makes sense for k/v responses. For other kinds of
// responses, i.e. MemberList, usually the revision isn't populated
// at all; so it would be better to hide this field in these cases.
- if h.Revision > 0 {
- fmt.Println(`"Revision" :`, h.Revision)
+ if h.GetRevision() > 0 {
+ fmt.Println(`"Revision" :`, h.GetRevision())
}
- fmt.Println(`"RaftTerm" :`, h.RaftTerm)
+ fmt.Println(`"RaftTerm" :`, h.GetRaftTerm())
}
-func (p *fieldsPrinter) Del(r v3.DeleteResponse) {
- p.hdr(r.Header)
- fmt.Println(`"Deleted" :`, r.Deleted)
- for _, kv := range r.PrevKvs {
+func (p *fieldsPrinter) Del(r *v3.DeleteResponse) {
+ resp := (*pb.DeleteRangeResponse)(r)
+ p.hdr(resp.GetHeader())
+ fmt.Println(`"Deleted" :`, resp.GetDeleted())
+ for _, kv := range resp.GetPrevKvs() {
p.kv("Prev", kv)
}
}
-func (p *fieldsPrinter) Get(r v3.GetResponse) {
- p.hdr(r.Header)
- for _, kv := range r.Kvs {
+func (p *fieldsPrinter) Get(r *v3.GetResponse) {
+ resp := (*pb.RangeResponse)(r)
+ p.hdr(resp.GetHeader())
+ for _, kv := range resp.GetKvs() {
p.kv("", kv)
}
- fmt.Println(`"More" :`, r.More)
- fmt.Println(`"Count" :`, r.Count)
+ fmt.Println(`"More" :`, resp.GetMore())
+ fmt.Println(`"Count" :`, resp.GetCount())
}
-func (p *fieldsPrinter) Put(r v3.PutResponse) {
- p.hdr(r.Header)
- if r.PrevKv != nil {
- p.kv("Prev", r.PrevKv)
+func (p *fieldsPrinter) Put(r *v3.PutResponse) {
+ resp := (*pb.PutResponse)(r)
+ p.hdr(resp.GetHeader())
+ if resp.GetPrevKv() != nil {
+ p.kv("Prev", resp.GetPrevKv())
}
}
-func (p *fieldsPrinter) Txn(r v3.TxnResponse) {
- p.hdr(r.Header)
- fmt.Println(`"Succeeded" :`, r.Succeeded)
- for _, resp := range r.Responses {
- switch v := resp.Response.(type) {
+func (p *fieldsPrinter) Txn(r *v3.TxnResponse) {
+ resp := (*pb.TxnResponse)(r)
+ p.hdr(resp.GetHeader())
+ fmt.Println(`"Succeeded" :`, resp.GetSucceeded())
+ for _, opResp := range resp.GetResponses() {
+ switch v := opResp.GetResponse().(type) {
case *pb.ResponseOp_ResponseDeleteRange:
- p.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
+ p.Del((*v3.DeleteResponse)(v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
- p.Put((v3.PutResponse)(*v.ResponsePut))
+ p.Put((*v3.PutResponse)(v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
- p.Get((v3.GetResponse)(*v.ResponseRange))
+ p.Get((*v3.GetResponse)(v.ResponseRange))
default:
fmt.Printf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v))
}
}
}
-func (p *fieldsPrinter) Watch(resp v3.WatchResponse) {
- p.hdr(&resp.Header)
- for _, e := range resp.Events {
- fmt.Println(`"Type" :`, e.Type)
- if e.PrevKv != nil {
- p.kv("Prev", e.PrevKv)
+func (p *fieldsPrinter) Watch(resp *v3.WatchResponse) {
+ if resp == nil {
+ return
+ }
+
+ p.hdr(resp.Header)
+ for _, ev := range resp.Events {
+ fmt.Println(`"Type" :`, ev.GetType())
+ if ev.GetPrevKv() != nil {
+ p.kv("Prev", ev.GetPrevKv())
}
- p.kv("", e.Kv)
+ p.kv("", ev.GetKv())
}
}
-func (p *fieldsPrinter) Grant(r v3.LeaseGrantResponse) {
+func (p *fieldsPrinter) Grant(r *v3.LeaseGrantResponse) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
@@ -120,11 +128,11 @@ func (p *fieldsPrinter) Grant(r v3.LeaseGrantResponse) {
fmt.Println(`"TTL" :`, r.TTL)
}
-func (p *fieldsPrinter) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) {
- p.hdr(r.Header)
+func (p *fieldsPrinter) Revoke(id v3.LeaseID, r *v3.LeaseRevokeResponse) {
+ p.hdr((*pb.LeaseRevokeResponse)(r).GetHeader())
}
-func (p *fieldsPrinter) KeepAlive(r v3.LeaseKeepAliveResponse) {
+func (p *fieldsPrinter) KeepAlive(r *v3.LeaseKeepAliveResponse) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
@@ -134,7 +142,7 @@ func (p *fieldsPrinter) KeepAlive(r v3.LeaseKeepAliveResponse) {
fmt.Println(`"TTL" :`, r.TTL)
}
-func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
+func (p *fieldsPrinter) TimeToLive(r *v3.LeaseTimeToLiveResponse, keys bool) {
p.hdr(r.ResponseHeader)
if p.isHex {
fmt.Printf("\"ID\" : %016x\n", r.ID)
@@ -148,7 +156,7 @@ func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
}
}
-func (p *fieldsPrinter) Leases(r v3.LeaseLeasesResponse) {
+func (p *fieldsPrinter) Leases(r *v3.LeaseLeasesResponse) {
p.hdr(r.ResponseHeader)
for _, item := range r.Leases {
if p.isHex {
@@ -159,22 +167,23 @@ func (p *fieldsPrinter) Leases(r v3.LeaseLeasesResponse) {
}
}
-func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
- p.hdr(r.Header)
- for _, m := range r.Members {
+func (p *fieldsPrinter) MemberList(r *v3.MemberListResponse) {
+ resp := (*pb.MemberListResponse)(r)
+ p.hdr(resp.GetHeader())
+ for _, m := range resp.GetMembers() {
if p.isHex {
- fmt.Println(`"ID" :`, types.ID(m.ID))
+ fmt.Println(`"ID" :`, types.ID(m.GetID()))
} else {
- fmt.Println(`"ID" :`, m.ID)
+ fmt.Println(`"ID" :`, m.GetID())
}
- fmt.Printf("\"Name\" : %q\n", m.Name)
- for _, u := range m.PeerURLs {
+ fmt.Printf("\"Name\" : %q\n", m.GetName())
+ for _, u := range m.GetPeerURLs() {
fmt.Printf("\"PeerURL\" : %q\n", u)
}
- for _, u := range m.ClientURLs {
+ for _, u := range m.GetClientURLs() {
fmt.Printf("\"ClientURL\" : %q\n", u)
}
- fmt.Println(`"IsLearner" :`, m.IsLearner)
+ fmt.Println(`"IsLearner" :`, m.GetIsLearner())
fmt.Println()
}
}
@@ -191,81 +200,103 @@ func (p *fieldsPrinter) EndpointHealth(hs []epHealth) {
func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
for _, ep := range eps {
- p.hdr(ep.Resp.Header)
- fmt.Printf("\"Version\" : %q\n", ep.Resp.Version)
- fmt.Printf("\"StorageVersion\" : %q\n", ep.Resp.StorageVersion)
- fmt.Println(`"DBSize" :`, ep.Resp.DbSize)
- fmt.Println(`"DBSizeInUse" :`, ep.Resp.DbSizeInUse)
- fmt.Println(`"DBSizeQuota" :`, ep.Resp.DbSizeQuota)
- fmt.Println(`"Leader" :`, ep.Resp.Leader)
- fmt.Println(`"IsLearner" :`, ep.Resp.IsLearner)
- fmt.Println(`"RaftIndex" :`, ep.Resp.RaftIndex)
- fmt.Println(`"RaftTerm" :`, ep.Resp.RaftTerm)
- fmt.Println(`"RaftAppliedIndex" :`, ep.Resp.RaftAppliedIndex)
- fmt.Println(`"Errors" :`, ep.Resp.Errors)
+ resp := (*pb.StatusResponse)(ep.Resp)
+ p.hdr(resp.GetHeader())
+ fmt.Printf("\"Version\" : %q\n", resp.GetVersion())
+ fmt.Printf("\"StorageVersion\" : %q\n", resp.GetStorageVersion())
+ fmt.Println(`"DBSize" :`, resp.GetDbSize())
+ fmt.Println(`"DBSizeInUse" :`, resp.GetDbSizeInUse())
+ fmt.Println(`"DBSizeQuota" :`, resp.GetDbSizeQuota())
+ fmt.Println(`"Leader" :`, resp.GetLeader())
+ fmt.Println(`"IsLearner" :`, resp.GetIsLearner())
+ fmt.Println(`"RaftIndex" :`, resp.GetRaftIndex())
+ fmt.Println(`"RaftTerm" :`, resp.GetRaftTerm())
+ fmt.Println(`"RaftAppliedIndex" :`, resp.GetRaftAppliedIndex())
+ fmt.Println(`"Errors" :`, resp.GetErrors())
fmt.Printf("\"Endpoint\" : %q\n", ep.Ep)
- fmt.Printf("\"DowngradeTargetVersion\" : %q\n", ep.Resp.DowngradeInfo.GetTargetVersion())
- fmt.Println(`"DowngradeEnabled" :`, ep.Resp.DowngradeInfo.GetEnabled())
+ fmt.Printf("\"DowngradeTargetVersion\" : %q\n", resp.GetDowngradeInfo().GetTargetVersion())
+ fmt.Println(`"DowngradeEnabled" :`, resp.GetDowngradeInfo().GetEnabled())
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointHashKV(hs []epHashKV) {
for _, h := range hs {
- p.hdr(h.Resp.Header)
+ resp := (*pb.HashKVResponse)(h.Resp)
+ p.hdr(resp.GetHeader())
fmt.Printf("\"Endpoint\" : %q\n", h.Ep)
- fmt.Println(`"Hash" :`, h.Resp.Hash)
- fmt.Println(`"HashRevision" :`, h.Resp.HashRevision)
+ fmt.Println(`"Hash" :`, resp.GetHash())
+ fmt.Println(`"HashRevision" :`, resp.GetHashRevision())
fmt.Println()
}
}
-func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) {
- p.hdr(r.Header)
- for _, a := range r.Alarms {
+func (p *fieldsPrinter) Alarm(r *v3.AlarmResponse) {
+ resp := (*pb.AlarmResponse)(r)
+ p.hdr(resp.GetHeader())
+ for _, a := range resp.GetAlarms() {
if p.isHex {
- fmt.Println(`"MemberID" :`, types.ID(a.MemberID))
+ fmt.Println(`"MemberID" :`, types.ID(a.GetMemberID()))
} else {
- fmt.Println(`"MemberID" :`, a.MemberID)
+ fmt.Println(`"MemberID" :`, a.GetMemberID())
}
- fmt.Println(`"AlarmType" :`, a.Alarm)
+ fmt.Println(`"AlarmType" :`, a.GetAlarm())
fmt.Println()
}
}
-func (p *fieldsPrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) { p.hdr(r.Header) }
-func (p *fieldsPrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
- p.hdr(r.Header)
- for _, p := range r.Perm {
- fmt.Println(`"PermType" : `, p.PermType.String())
- fmt.Printf("\"Key\" : %q\n", string(p.Key))
- fmt.Printf("\"RangeEnd\" : %q\n", string(p.RangeEnd))
+func (p *fieldsPrinter) RoleAdd(role string, r *v3.AuthRoleAddResponse) {
+ p.hdr((*pb.AuthRoleAddResponse)(r).GetHeader())
+}
+
+func (p *fieldsPrinter) RoleGet(role string, r *v3.AuthRoleGetResponse) {
+ resp := (*pb.AuthRoleGetResponse)(r)
+ p.hdr(resp.GetHeader())
+ for _, perm := range resp.GetPerm() {
+ fmt.Println(`"PermType" : `, perm.GetPermType().String())
+ fmt.Printf("\"Key\" : %q\n", string(perm.GetKey()))
+ fmt.Printf("\"RangeEnd\" : %q\n", string(perm.GetRangeEnd()))
}
}
-func (p *fieldsPrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) { p.hdr(r.Header) }
-func (p *fieldsPrinter) RoleList(r v3.AuthRoleListResponse) {
- p.hdr(r.Header)
+
+func (p *fieldsPrinter) RoleDelete(role string, r *v3.AuthRoleDeleteResponse) {
+ p.hdr((*pb.AuthRoleDeleteResponse)(r).GetHeader())
+}
+
+func (p *fieldsPrinter) RoleList(r *v3.AuthRoleListResponse) {
+ resp := (*pb.AuthRoleListResponse)(r)
+ p.hdr(resp.GetHeader())
fmt.Print(`"Roles" :`)
- for _, r := range r.Roles {
- fmt.Printf(" %q", r)
+ for _, role := range resp.GetRoles() {
+ fmt.Printf(" %q", role)
}
fmt.Println()
}
-func (p *fieldsPrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
- p.hdr(r.Header)
+func (p *fieldsPrinter) RoleGrantPermission(role string, r *v3.AuthRoleGrantPermissionResponse) {
+ p.hdr((*pb.AuthRoleGrantPermissionResponse)(r).GetHeader())
}
-func (p *fieldsPrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
- p.hdr(r.Header)
+func (p *fieldsPrinter) RoleRevokePermission(role string, key string, end string, r *v3.AuthRoleRevokePermissionResponse) {
+ p.hdr((*pb.AuthRoleRevokePermissionResponse)(r).GetHeader())
}
-func (p *fieldsPrinter) UserAdd(user string, r v3.AuthUserAddResponse) { p.hdr(r.Header) }
-func (p *fieldsPrinter) UserChangePassword(r v3.AuthUserChangePasswordResponse) { p.hdr(r.Header) }
-func (p *fieldsPrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
- p.hdr(r.Header)
+
+func (p *fieldsPrinter) UserAdd(user string, r *v3.AuthUserAddResponse) {
+ p.hdr((*pb.AuthUserAddResponse)(r).GetHeader())
+}
+
+func (p *fieldsPrinter) UserChangePassword(r *v3.AuthUserChangePasswordResponse) {
+ p.hdr((*pb.AuthUserChangePasswordResponse)(r).GetHeader())
+}
+
+func (p *fieldsPrinter) UserGrantRole(user string, role string, r *v3.AuthUserGrantRoleResponse) {
+ p.hdr((*pb.AuthUserGrantRoleResponse)(r).GetHeader())
+}
+
+func (p *fieldsPrinter) UserRevokeRole(user string, role string, r *v3.AuthUserRevokeRoleResponse) {
+ p.hdr((*pb.AuthUserRevokeRoleResponse)(r).GetHeader())
}
-func (p *fieldsPrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
- p.hdr(r.Header)
+func (p *fieldsPrinter) UserDelete(user string, r *v3.AuthUserDeleteResponse) {
+ p.hdr((*pb.AuthUserDeleteResponse)(r).GetHeader())
}
-func (p *fieldsPrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) { p.hdr(r.Header) }
diff --git a/etcdctl/ctlv3/command/printer_json.go b/etcdctl/ctlv3/command/printer_json.go
index 49abb35ff5f1..81c7bbbce9d6 100644
--- a/etcdctl/ctlv3/command/printer_json.go
+++ b/etcdctl/ctlv3/command/printer_json.go
@@ -17,20 +17,57 @@ package command
import (
"encoding/json"
"fmt"
+ "io"
"os"
- "strconv"
- "strings"
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
type jsonPrinter struct {
- isHex bool
+ writer io.Writer
+ isHex bool
printer
}
+type (
+ HexResponseHeader pb.ResponseHeader
+ HexMember pb.Member
+)
+
+func (h *HexResponseHeader) MarshalJSON() ([]byte, error) {
+ return json.Marshal(&struct {
+ ClusterID string `json:"cluster_id"`
+ MemberID string `json:"member_id"`
+ Revision int64 `json:"revision,omitempty"`
+ RaftTerm uint64 `json:"raft_term,omitempty"`
+ }{
+ ClusterID: fmt.Sprintf("%x", h.ClusterId),
+ MemberID: fmt.Sprintf("%x", h.MemberId),
+ Revision: h.Revision,
+ RaftTerm: h.RaftTerm,
+ })
+}
+
+func (m *HexMember) MarshalJSON() ([]byte, error) {
+ return json.Marshal(&struct {
+ ID string `json:"ID"`
+ Name string `json:"name,omitempty"`
+ PeerURLs []string `json:"peerURLs,omitempty"`
+ ClientURLs []string `json:"clientURLs,omitempty"`
+ IsLearner bool `json:"isLearner,omitempty"`
+ }{
+ ID: fmt.Sprintf("%x", m.ID),
+ Name: m.Name,
+ PeerURLs: m.PeerURLs,
+ ClientURLs: m.ClientURLs,
+ IsLearner: m.IsLearner,
+ })
+}
+
func newJSONPrinter(isHex bool) printer {
return &jsonPrinter{
+ writer: os.Stdout,
isHex: isHex,
printer: &printerRPC{newPrinterUnsupported("json"), printJSON},
}
@@ -40,61 +77,157 @@ func (p *jsonPrinter) EndpointHealth(r []epHealth) { printJSON(r) }
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) }
-func (p *jsonPrinter) MemberList(r clientv3.MemberListResponse) {
- if p.isHex {
- printMemberListWithHexJSON(r)
- } else {
- printJSON(r)
- }
+func (p *jsonPrinter) MemberAdd(r *clientv3.MemberAddResponse) { p.printJSON(r) }
+func (p *jsonPrinter) MemberRemove(_ uint64, r *clientv3.MemberRemoveResponse) { p.printJSON(r) }
+func (p *jsonPrinter) MemberUpdate(_ uint64, r *clientv3.MemberUpdateResponse) { p.printJSON(r) }
+func (p *jsonPrinter) MemberPromote(_ uint64, r *clientv3.MemberPromoteResponse) { p.printJSON(r) }
+func (p *jsonPrinter) MemberList(r *clientv3.MemberListResponse) { p.printJSON(r) }
+
+func (p *jsonPrinter) Txn(r *clientv3.TxnResponse) {
+ p.printJSON(TxnResponseJSONFromProto((*pb.TxnResponse)(r)))
}
-func printJSON(v any) {
+func printJSONTo(w io.Writer, v any) {
b, err := json.Marshal(v)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
- fmt.Println(string(b))
+ fmt.Fprintln(w, string(b))
+}
+
+func printJSON(v any) {
+ printJSONTo(os.Stdout, v)
+}
+
+type TxnResponseJSON struct {
+ Header *pb.ResponseHeader `json:"header,omitempty"`
+ Succeeded bool `json:"succeeded,omitempty"`
+ Responses []ResponseOpJSON `json:"responses,omitempty"`
+}
+
+type ResponseOpJSON struct {
+ Response ResponseOpResponseJSON `json:"Response"`
+}
+
+type ResponseOpResponseJSON struct {
+ ResponseRange *pb.RangeResponse `json:"response_range,omitempty"`
+ ResponsePut *pb.PutResponse `json:"response_put,omitempty"`
+ ResponseDeleteRange *pb.DeleteRangeResponse `json:"response_delete_range,omitempty"`
+ ResponseTxn *TxnResponseJSON `json:"response_txn,omitempty"`
+}
+
+func (t *TxnResponseJSON) ToProto() *pb.TxnResponse {
+ if t == nil {
+ return nil
+ }
+
+ r := &pb.TxnResponse{
+ Header: t.Header,
+ Succeeded: t.Succeeded,
+ }
+ for _, jsonResponse := range t.Responses {
+ switch {
+ case jsonResponse.Response.ResponseDeleteRange != nil:
+ r.Responses = append(r.Responses, &pb.ResponseOp{Response: &pb.ResponseOp_ResponseDeleteRange{ResponseDeleteRange: jsonResponse.Response.ResponseDeleteRange}})
+ case jsonResponse.Response.ResponseRange != nil:
+ r.Responses = append(r.Responses, &pb.ResponseOp{Response: &pb.ResponseOp_ResponseRange{ResponseRange: jsonResponse.Response.ResponseRange}})
+ case jsonResponse.Response.ResponseTxn != nil:
+ r.Responses = append(r.Responses, &pb.ResponseOp{Response: &pb.ResponseOp_ResponseTxn{ResponseTxn: jsonResponse.Response.ResponseTxn.ToProto()}})
+ case jsonResponse.Response.ResponsePut != nil:
+ r.Responses = append(r.Responses, &pb.ResponseOp{Response: &pb.ResponseOp_ResponsePut{ResponsePut: jsonResponse.Response.ResponsePut}})
+ default:
+ // unknown
+ r.Responses = append(r.Responses, &pb.ResponseOp{})
+ }
+ }
+ return r
}
-func printMemberListWithHexJSON(r clientv3.MemberListResponse) {
- var buffer strings.Builder
- var b []byte
- buffer.WriteString("{\"header\":{\"cluster_id\":\"")
- b = strconv.AppendUint(nil, r.Header.ClusterId, 16)
- buffer.Write(b)
- buffer.WriteString("\",\"member_id\":\"")
- b = strconv.AppendUint(nil, r.Header.MemberId, 16)
- buffer.Write(b)
- buffer.WriteString("\",\"raft_term\":")
- b = strconv.AppendUint(nil, r.Header.RaftTerm, 10)
- buffer.Write(b)
- buffer.WriteByte('}')
- for i := 0; i < len(r.Members); i++ {
- if i == 0 {
- buffer.WriteString(",\"members\":[{\"ID\":\"")
- } else {
- buffer.WriteString(",{\"ID\":\"")
+func TxnResponseJSONFromProto(protoResponse *pb.TxnResponse) TxnResponseJSON {
+ r := TxnResponseJSON{
+ Header: protoResponse.GetHeader(),
+ Succeeded: protoResponse.GetSucceeded(),
+ }
+ for _, response := range protoResponse.GetResponses() {
+ switch response := response.Response.(type) {
+ case *pb.ResponseOp_ResponseRange:
+ r.Responses = append(r.Responses, ResponseOpJSON{ResponseOpResponseJSON{ResponseRange: response.ResponseRange}})
+ case *pb.ResponseOp_ResponsePut:
+ r.Responses = append(r.Responses, ResponseOpJSON{ResponseOpResponseJSON{ResponsePut: response.ResponsePut}})
+ case *pb.ResponseOp_ResponseDeleteRange:
+ r.Responses = append(r.Responses, ResponseOpJSON{ResponseOpResponseJSON{ResponseDeleteRange: response.ResponseDeleteRange}})
+ case *pb.ResponseOp_ResponseTxn:
+ responseTxn := TxnResponseJSONFromProto(response.ResponseTxn)
+ r.Responses = append(r.Responses, ResponseOpJSON{ResponseOpResponseJSON{ResponseTxn: &responseTxn}})
+ default:
+ r.Responses = append(r.Responses, ResponseOpJSON{ResponseOpResponseJSON{}})
}
- b = strconv.AppendUint(nil, r.Members[i].ID, 16)
- buffer.Write(b)
- buffer.WriteString("\",\"name\":\"" + r.Members[i].Name + "\"," + "\"peerURLs\":")
- b, err := json.Marshal(r.Members[i].PeerURLs)
- if err != nil {
- return
+ }
+ return r
+}
+
+func (p *jsonPrinter) printJSON(v any) {
+ var data any
+ if !p.isHex {
+ printJSONTo(p.writer, v)
+ return
+ }
+
+ switch r := v.(type) {
+ case *clientv3.MemberAddResponse:
+ data = &struct {
+ Header *HexResponseHeader `json:"header"`
+ Member *HexMember `json:"member"`
+ Members []*HexMember `json:"members"`
+ }{
+ Header: (*HexResponseHeader)(r.Header),
+ Member: (*HexMember)(r.Member),
+ Members: toHexMembers(r.Members),
+ }
+ case *clientv3.MemberRemoveResponse:
+ data = &struct {
+ Header *HexResponseHeader `json:"header"`
+ Members []*HexMember `json:"members"`
+ }{
+ Header: (*HexResponseHeader)(r.Header),
+ Members: toHexMembers(r.Members),
+ }
+ case *clientv3.MemberUpdateResponse:
+ data = &struct {
+ Header *HexResponseHeader `json:"header"`
+ Members []*HexMember `json:"members"`
+ }{
+ Header: (*HexResponseHeader)(r.Header),
+ Members: toHexMembers(r.Members),
}
- buffer.Write(b)
- buffer.WriteString(",\"clientURLs\":")
- b, err = json.Marshal(r.Members[i].ClientURLs)
- if err != nil {
- return
+ case *clientv3.MemberPromoteResponse:
+ data = &struct {
+ Header *HexResponseHeader `json:"header"`
+ Members []*HexMember `json:"members"`
+ }{
+ Header: (*HexResponseHeader)(r.Header),
+ Members: toHexMembers(r.Members),
}
- buffer.Write(b)
- buffer.WriteByte('}')
- if i == len(r.Members)-1 {
- buffer.WriteString("]")
+ case *clientv3.MemberListResponse:
+ data = &struct {
+ Header *HexResponseHeader `json:"header"`
+ Members []*HexMember `json:"members"`
+ }{
+ Header: (*HexResponseHeader)(r.Header),
+ Members: toHexMembers(r.Members),
}
+ default:
+ data = v
+ }
+
+ printJSONTo(p.writer, data)
+}
+
+func toHexMembers(members []*pb.Member) []*HexMember {
+ hexMembers := make([]*HexMember, len(members))
+ for i, member := range members {
+ hexMembers[i] = (*HexMember)(member)
}
- buffer.WriteString("}")
- fmt.Println(buffer.String())
+ return hexMembers
}
diff --git a/etcdctl/ctlv3/command/printer_json_test.go b/etcdctl/ctlv3/command/printer_json_test.go
new file mode 100644
index 000000000000..e883846a8d54
--- /dev/null
+++ b/etcdctl/ctlv3/command/printer_json_test.go
@@ -0,0 +1,375 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 command
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "math"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+const (
+ keyHeader = "header"
+ keyMember = "member"
+ keyMembers = "members"
+
+ keyClusterID = "cluster_id"
+ keyMemberID = "member_id"
+ keyRaftTerm = "raft_term"
+ keyRevision = "revision"
+ keyID = "ID"
+)
+
+func assertNumericFieldEqual(t *testing.T, obj map[string]any, key string, want int64) {
+ raw, ok := obj[key]
+ require.Truef(t, ok, "missing key %q in map %v", key, obj)
+
+ n, ok := raw.(json.Number)
+ require.Truef(t, ok, "field %q is not json.Number: %v", key, raw)
+
+ val, err := n.Int64()
+ require.NoErrorf(t, err, "failed to convert field %q to int64: %v", key, n)
+
+ assert.Equalf(t, want, val, "unexpected value for field %q", key)
+}
+
+func assertHexFieldEqual(t *testing.T, obj map[string]any, key string, want string) {
+ raw, ok := obj[key]
+ require.Truef(t, ok, "missing key %q in map %v", key, obj)
+
+ str, ok := raw.(string)
+ require.Truef(t, ok, "field %q is not a string: %v", key, str)
+
+ assert.Equalf(t, want, str, "unexpected value for hex field %q", key)
+}
+
+func assertHeader(t *testing.T, testGroup *testScenario, tt *testCase, got map[string]any) {
+ rawHeader, ok := got[keyHeader]
+ require.Truef(t, ok, "output does not contain %q field: %v", keyHeader, got)
+ header, ok := rawHeader.(map[string]any)
+ require.Truef(t, ok, "field %q is not map[string]any: %v", keyHeader, rawHeader)
+
+ if testGroup.isHex {
+ assertHexFieldEqual(t, header, keyClusterID, tt.wantHexString)
+ assertHexFieldEqual(t, header, keyMemberID, tt.wantHexString)
+ } else {
+ assertNumericFieldEqual(t, header, keyClusterID, tt.wantDecimalNumber)
+ assertNumericFieldEqual(t, header, keyMemberID, tt.wantDecimalNumber)
+ }
+ assertNumericFieldEqual(t, header, keyRaftTerm, tt.wantDecimalNumber)
+ assertNumericFieldEqual(t, header, keyRevision, tt.wantDecimalNumber)
+}
+
+func assertMember(t *testing.T, testGroup *testScenario, tt *testCase, rawMember any) {
+ member, ok := rawMember.(map[string]any)
+ require.Truef(t, ok, "field %q is not map[string]any: %v", keyMember, rawMember)
+
+ if testGroup.isHex {
+ assertHexFieldEqual(t, member, keyID, tt.wantHexString)
+ } else {
+ assertNumericFieldEqual(t, member, keyID, tt.wantDecimalNumber)
+ }
+}
+
+func assertMembers(t *testing.T, testGroup *testScenario, tt *testCase, got map[string]any) {
+ rawMembers, ok := got[keyMembers]
+ require.Truef(t, ok, "output does not contain %q field: %v", keyMembers, got)
+ members, ok := rawMembers.([]any)
+ require.Truef(t, ok, "field %q is not []any: %v", keyMembers, rawMembers)
+
+ for _, rawMember := range members {
+ assertMember(t, testGroup, tt, rawMember)
+ }
+}
+
+type testCase struct {
+ number uint64
+ wantHexString string
+ wantDecimalNumber int64
+}
+
+type testScenario struct {
+ name string
+ isHex bool
+ cases []testCase
+}
+
+var testCases = []testCase{
+ {1, "1", 1},
+ {100, "64", 100},
+ {1234567890, "499602d2", 1234567890},
+ {math.MaxInt64, "7fffffffffffffff", math.MaxInt64},
+}
+
+// TODO(fuweid): This helper is used to make sure that adaptor struct is
+// compatible with gogopb struct. We should remove this once we migrate to
+// official pb.
+func TestTxnResponseJSONFailedResponseMatchesProtoJSON(t *testing.T) {
+ response := &pb.TxnResponse{
+ Header: &pb.ResponseHeader{Revision: 1},
+ Succeeded: false,
+ }
+
+ want, err := json.Marshal(response)
+ require.NoError(t, err)
+ got, err := json.Marshal(TxnResponseJSONFromProto(response))
+ require.NoError(t, err)
+
+ assert.JSONEq(t, string(want), string(got))
+}
+
+// TODO(fuweid): This helper is used to make sure that adaptor struct is
+// compatible with gogopb struct. We should remove this once we migrate to
+// official pb.
+func TestTxnResponseJSONNestedTxnRoundTrip(t *testing.T) {
+ response := &pb.TxnResponse{
+ Succeeded: true,
+ Responses: []*pb.ResponseOp{{
+ Response: &pb.ResponseOp_ResponseTxn{
+ ResponseTxn: &pb.TxnResponse{
+ Succeeded: true,
+ Responses: []*pb.ResponseOp{{
+ Response: &pb.ResponseOp_ResponseRange{
+ ResponseRange: &pb.RangeResponse{Count: 1},
+ },
+ }},
+ },
+ },
+ }},
+ }
+
+ data, err := json.Marshal(TxnResponseJSONFromProto(response))
+ require.NoError(t, err)
+
+ var got TxnResponseJSON
+ decoder := json.NewDecoder(bytes.NewReader(data))
+ require.NoError(t, decoder.Decode(&got))
+
+ assert.Equal(t, response, got.ToProto())
+}
+
+func TestMemberAdd(t *testing.T) {
+ tests := []testScenario{
+ {name: "decimal", isHex: false, cases: testCases},
+ {name: "hex", isHex: true, cases: testCases},
+ }
+
+ for _, testGroup := range tests {
+ t.Run(testGroup.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
+
+ for _, tt := range testGroup.cases {
+ t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
+ buffer.Reset()
+ decoder := json.NewDecoder(&buffer)
+ decoder.UseNumber()
+
+ response := clientv3.MemberAddResponse{
+ Header: &pb.ResponseHeader{
+ ClusterId: tt.number,
+ MemberId: tt.number,
+ Revision: int64(tt.number),
+ RaftTerm: tt.number,
+ },
+ Member: &pb.Member{ID: tt.number},
+ Members: []*pb.Member{{ID: tt.number}},
+ }
+ p.MemberAdd(&response)
+
+ var got map[string]any
+ err := decoder.Decode(&got)
+ require.NoErrorf(t, err, "failed to decode JSON")
+
+ assertHeader(t, &testGroup, &tt, got)
+
+ rawMember, ok := got[keyMember]
+ require.Truef(t, ok, "output does not contain %q field: %v", keyMember, got)
+ assertMember(t, &testGroup, &tt, rawMember)
+
+ assertMembers(t, &testGroup, &tt, got)
+ })
+ }
+ })
+ }
+}
+
+func TestMemberRemove(t *testing.T) {
+ tests := []testScenario{
+ {name: "decimal", isHex: false, cases: testCases},
+ {name: "hex", isHex: true, cases: testCases},
+ }
+
+ for _, testGroup := range tests {
+ t.Run(testGroup.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
+
+ for _, tt := range testGroup.cases {
+ t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
+ buffer.Reset()
+ decoder := json.NewDecoder(&buffer)
+ decoder.UseNumber()
+
+ response := clientv3.MemberRemoveResponse{
+ Header: &pb.ResponseHeader{
+ ClusterId: tt.number,
+ MemberId: tt.number,
+ Revision: int64(tt.number),
+ RaftTerm: tt.number,
+ },
+ Members: []*pb.Member{{ID: tt.number}},
+ }
+ p.MemberRemove(0, &response)
+
+ var got map[string]any
+ err := decoder.Decode(&got)
+ require.NoErrorf(t, err, "failed to decode JSON")
+
+ assertHeader(t, &testGroup, &tt, got)
+ assertMembers(t, &testGroup, &tt, got)
+ })
+ }
+ })
+ }
+}
+
+func TestMemberUpdate(t *testing.T) {
+ tests := []testScenario{
+ {name: "decimal", isHex: false, cases: testCases},
+ {name: "hex", isHex: true, cases: testCases},
+ }
+
+ for _, testGroup := range tests {
+ t.Run(testGroup.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
+
+ for _, tt := range testGroup.cases {
+ t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
+ buffer.Reset()
+ decoder := json.NewDecoder(&buffer)
+ decoder.UseNumber()
+
+ response := clientv3.MemberUpdateResponse{
+ Header: &pb.ResponseHeader{
+ ClusterId: tt.number,
+ MemberId: tt.number,
+ Revision: int64(tt.number),
+ RaftTerm: tt.number,
+ },
+ Members: []*pb.Member{{ID: tt.number}},
+ }
+ p.MemberUpdate(0, &response)
+
+ var got map[string]any
+ err := decoder.Decode(&got)
+ require.NoErrorf(t, err, "failed to decode JSON")
+
+ assertHeader(t, &testGroup, &tt, got)
+ assertMembers(t, &testGroup, &tt, got)
+ })
+ }
+ })
+ }
+}
+
+func TestMemberPromote(t *testing.T) {
+ tests := []testScenario{
+ {name: "decimal", isHex: false, cases: testCases},
+ {name: "hex", isHex: true, cases: testCases},
+ }
+
+ for _, testGroup := range tests {
+ t.Run(testGroup.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
+
+ for _, tt := range testGroup.cases {
+ t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
+ buffer.Reset()
+ decoder := json.NewDecoder(&buffer)
+ decoder.UseNumber()
+
+ response := clientv3.MemberPromoteResponse{
+ Header: &pb.ResponseHeader{
+ ClusterId: tt.number,
+ MemberId: tt.number,
+ Revision: int64(tt.number),
+ RaftTerm: tt.number,
+ },
+ Members: []*pb.Member{{ID: tt.number}},
+ }
+ p.MemberPromote(0, &response)
+
+ var got map[string]any
+ err := decoder.Decode(&got)
+ require.NoErrorf(t, err, "failed to decode JSON")
+
+ assertHeader(t, &testGroup, &tt, got)
+ assertMembers(t, &testGroup, &tt, got)
+ })
+ }
+ })
+ }
+}
+
+func TestMemberList(t *testing.T) {
+ tests := []testScenario{
+ {name: "decimal", isHex: false, cases: testCases},
+ {name: "hex", isHex: true, cases: testCases},
+ }
+
+ for _, testGroup := range tests {
+ t.Run(testGroup.name, func(t *testing.T) {
+ var buffer bytes.Buffer
+ p := &jsonPrinter{writer: &buffer, isHex: testGroup.isHex}
+
+ for _, tt := range testGroup.cases {
+ t.Run(fmt.Sprintf("number=%d", tt.number), func(t *testing.T) {
+ buffer.Reset()
+ decoder := json.NewDecoder(&buffer)
+ decoder.UseNumber()
+
+ response := clientv3.MemberListResponse{
+ Header: &pb.ResponseHeader{
+ ClusterId: tt.number,
+ MemberId: tt.number,
+ Revision: int64(tt.number),
+ RaftTerm: tt.number,
+ },
+ Members: []*pb.Member{{ID: tt.number}},
+ }
+ p.MemberList(&response)
+
+ var got map[string]any
+ err := decoder.Decode(&got)
+ require.NoErrorf(t, err, "failed to decode JSON")
+
+ assertHeader(t, &testGroup, &tt, got)
+ assertMembers(t, &testGroup, &tt, got)
+ })
+ }
+ })
+ }
+}
diff --git a/etcdctl/ctlv3/command/printer_protobuf.go b/etcdctl/ctlv3/command/printer_protobuf.go
index 3c839bffc8c1..11ba7762df61 100644
--- a/etcdctl/ctlv3/command/printer_protobuf.go
+++ b/etcdctl/ctlv3/command/printer_protobuf.go
@@ -18,45 +18,44 @@ import (
"fmt"
"os"
+ "google.golang.org/protobuf/proto"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
type pbPrinter struct{ printer }
-type pbMarshal interface {
- Marshal() ([]byte, error)
-}
-
func newPBPrinter() printer {
return &pbPrinter{
&printerRPC{newPrinterUnsupported("protobuf"), printPB},
}
}
-func (p *pbPrinter) Watch(r v3.WatchResponse) {
- evs := make([]*mvccpb.Event, len(r.Events))
- for i, ev := range r.Events {
- evs[i] = (*mvccpb.Event)(ev)
- }
- wr := pb.WatchResponse{
- Header: &r.Header,
- Events: evs,
- CompactRevision: r.CompactRevision,
- Canceled: r.Canceled,
- Created: r.Created,
+func (p *pbPrinter) Watch(r *v3.WatchResponse) {
+ wr := &pb.WatchResponse{}
+ if r != nil {
+ wr = &pb.WatchResponse{
+ Header: r.Header,
+ Events: r.Events,
+ CompactRevision: r.CompactRevision,
+ Canceled: r.Canceled,
+ Created: r.Created,
+ }
}
- printPB(&wr)
+ printPB(wr)
}
func printPB(v any) {
- m, ok := v.(pbMarshal)
- if !ok {
+ var b []byte
+ var err error
+ switch m := v.(type) {
+ case proto.Message:
+ b, err = proto.Marshal(m)
+ default:
cobrautl.ExitWithError(cobrautl.ExitBadFeature, fmt.Errorf("marshal unsupported for type %T (%v)", v, v))
}
- b, err := m.Marshal()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go
index c7e4fab725e2..35af58047bc4 100644
--- a/etcdctl/ctlv3/command/printer_simple.go
+++ b/etcdctl/ctlv3/command/printer_simple.go
@@ -19,6 +19,7 @@ import (
"os"
"strings"
+ "go.etcd.io/etcd/api/v3/authpb"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
v3 "go.etcd.io/etcd/client/v3"
@@ -31,71 +32,75 @@ type simplePrinter struct {
valueOnly bool
}
-func (s *simplePrinter) Del(resp v3.DeleteResponse) {
- fmt.Println(resp.Deleted)
- for _, kv := range resp.PrevKvs {
+func (s *simplePrinter) Del(resp *v3.DeleteResponse) {
+ r := (*pb.DeleteRangeResponse)(resp)
+ fmt.Println(r.GetDeleted())
+ for _, kv := range r.GetPrevKvs() {
printKV(s.isHex, s.valueOnly, kv)
}
}
-func (s *simplePrinter) Get(resp v3.GetResponse) {
- for _, kv := range resp.Kvs {
+func (s *simplePrinter) Get(resp *v3.GetResponse) {
+ r := (*pb.RangeResponse)(resp)
+ for _, kv := range r.GetKvs() {
printKV(s.isHex, s.valueOnly, kv)
}
}
-func (s *simplePrinter) Put(r v3.PutResponse) {
+func (s *simplePrinter) Put(r *v3.PutResponse) {
+ resp := (*pb.PutResponse)(r)
fmt.Println("OK")
- if r.PrevKv != nil {
- printKV(s.isHex, s.valueOnly, r.PrevKv)
+ if resp.GetPrevKv() != nil {
+ printKV(s.isHex, s.valueOnly, resp.GetPrevKv())
}
}
-func (s *simplePrinter) Txn(resp v3.TxnResponse) {
- if resp.Succeeded {
+func (s *simplePrinter) Txn(resp *v3.TxnResponse) {
+ r := (*pb.TxnResponse)(resp)
+ if r.GetSucceeded() {
fmt.Println("SUCCESS")
} else {
fmt.Println("FAILURE")
}
- for _, r := range resp.Responses {
+ for _, opResp := range r.GetResponses() {
fmt.Println("")
- switch v := r.Response.(type) {
+ switch v := opResp.GetResponse().(type) {
case *pb.ResponseOp_ResponseDeleteRange:
- s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
+ s.Del((*v3.DeleteResponse)(v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
- s.Put((v3.PutResponse)(*v.ResponsePut))
+ s.Put((*v3.PutResponse)(v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
- s.Get(((v3.GetResponse)(*v.ResponseRange)))
+ s.Get((*v3.GetResponse)(v.ResponseRange))
default:
- fmt.Printf("unexpected response %+v\n", r)
+ fmt.Printf("unexpected response %+v\n", opResp)
}
}
}
-func (s *simplePrinter) Watch(resp v3.WatchResponse) {
- for _, e := range resp.Events {
- fmt.Println(e.Type)
- if e.PrevKv != nil {
- printKV(s.isHex, s.valueOnly, e.PrevKv)
+func (s *simplePrinter) Watch(resp *v3.WatchResponse) {
+ for _, event := range resp.Events {
+ fmt.Println(event.GetType())
+ if event.GetPrevKv() != nil {
+ printKV(s.isHex, s.valueOnly, event.GetPrevKv())
}
- printKV(s.isHex, s.valueOnly, e.Kv)
+ printKV(s.isHex, s.valueOnly, event.GetKv())
}
}
-func (s *simplePrinter) Grant(resp v3.LeaseGrantResponse) {
+func (s *simplePrinter) Grant(resp *v3.LeaseGrantResponse) {
fmt.Printf("lease %016x granted with TTL(%ds)\n", resp.ID, resp.TTL)
}
-func (s *simplePrinter) Revoke(id v3.LeaseID, r v3.LeaseRevokeResponse) {
+func (s *simplePrinter) Revoke(id v3.LeaseID, r *v3.LeaseRevokeResponse) {
fmt.Printf("lease %016x revoked\n", id)
}
-func (s *simplePrinter) KeepAlive(resp v3.LeaseKeepAliveResponse) {
+func (s *simplePrinter) KeepAlive(resp *v3.LeaseKeepAliveResponse) {
fmt.Printf("lease %016x keepalived with TTL(%d)\n", resp.ID, resp.TTL)
}
-func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
+func (s *simplePrinter) TimeToLive(resp *v3.LeaseTimeToLiveResponse, keys bool) {
if resp.GrantedTTL == 0 && resp.TTL == -1 {
fmt.Printf("lease %016x already expired\n", resp.ID)
return
@@ -112,40 +117,42 @@ func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
fmt.Println(txt)
}
-func (s *simplePrinter) Leases(resp v3.LeaseLeasesResponse) {
+func (s *simplePrinter) Leases(resp *v3.LeaseLeasesResponse) {
fmt.Printf("found %d leases\n", len(resp.Leases))
for _, item := range resp.Leases {
fmt.Printf("%016x\n", item.ID)
}
}
-func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
- for _, e := range resp.Alarms {
+func (s *simplePrinter) Alarm(resp *v3.AlarmResponse) {
+ r := (*pb.AlarmResponse)(resp)
+ for _, e := range r.GetAlarms() {
fmt.Printf("%+v\n", e)
}
}
-func (s *simplePrinter) MemberAdd(r v3.MemberAddResponse) {
+func (s *simplePrinter) MemberAdd(r *v3.MemberAddResponse) {
+ resp := (*pb.MemberAddResponse)(r)
asLearner := " "
- if r.Member.IsLearner {
+ if resp.GetMember().GetIsLearner() {
asLearner = " as learner "
}
- fmt.Printf("Member %16x added%sto cluster %16x\n", r.Member.ID, asLearner, r.Header.ClusterId)
+ fmt.Printf("Member %16x added%sto cluster %16x\n", resp.GetMember().GetID(), asLearner, resp.GetHeader().GetClusterId())
}
-func (s *simplePrinter) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
- fmt.Printf("Member %16x removed from cluster %16x\n", id, r.Header.ClusterId)
+func (s *simplePrinter) MemberRemove(id uint64, r *v3.MemberRemoveResponse) {
+ fmt.Printf("Member %16x removed from cluster %16x\n", id, (*pb.MemberRemoveResponse)(r).GetHeader().GetClusterId())
}
-func (s *simplePrinter) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
- fmt.Printf("Member %16x updated in cluster %16x\n", id, r.Header.ClusterId)
+func (s *simplePrinter) MemberUpdate(id uint64, r *v3.MemberUpdateResponse) {
+ fmt.Printf("Member %16x updated in cluster %16x\n", id, (*pb.MemberUpdateResponse)(r).GetHeader().GetClusterId())
}
-func (s *simplePrinter) MemberPromote(id uint64, r v3.MemberPromoteResponse) {
- fmt.Printf("Member %16x promoted in cluster %16x\n", id, r.Header.ClusterId)
+func (s *simplePrinter) MemberPromote(id uint64, r *v3.MemberPromoteResponse) {
+ fmt.Printf("Member %16x promoted in cluster %16x\n", id, (*pb.MemberPromoteResponse)(r).GetHeader().GetClusterId())
}
-func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
+func (s *simplePrinter) MemberList(resp *v3.MemberListResponse) {
_, rows := makeMemberListTable(resp)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
@@ -176,29 +183,30 @@ func (s *simplePrinter) EndpointHashKV(hashList []epHashKV) {
}
}
-func (s *simplePrinter) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) {
+func (s *simplePrinter) MoveLeader(leader, target uint64, r *v3.MoveLeaderResponse) {
fmt.Printf("Leadership transferred from %s to %s\n", types.ID(leader), types.ID(target))
}
-func (s *simplePrinter) DowngradeValidate(r v3.DowngradeResponse) {
- fmt.Printf("Downgrade validate success, cluster version %s\n", r.Version)
+func (s *simplePrinter) DowngradeValidate(r *v3.DowngradeResponse) {
+ fmt.Printf("Downgrade validate success, cluster version %s\n", (*pb.DowngradeResponse)(r).GetVersion())
}
-func (s *simplePrinter) DowngradeEnable(r v3.DowngradeResponse) {
- fmt.Printf("Downgrade enable success, cluster version %s\n", r.Version)
+func (s *simplePrinter) DowngradeEnable(r *v3.DowngradeResponse) {
+ fmt.Printf("Downgrade enable success, cluster version %s\n", (*pb.DowngradeResponse)(r).GetVersion())
}
-func (s *simplePrinter) DowngradeCancel(r v3.DowngradeResponse) {
- fmt.Printf("Downgrade cancel success, cluster version %s\n", r.Version)
+func (s *simplePrinter) DowngradeCancel(r *v3.DowngradeResponse) {
+ fmt.Printf("Downgrade cancel success, cluster version %s\n", (*pb.DowngradeResponse)(r).GetVersion())
}
-func (s *simplePrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) {
+func (s *simplePrinter) RoleAdd(role string, r *v3.AuthRoleAddResponse) {
fmt.Printf("Role %s created\n", role)
}
-func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
+func (s *simplePrinter) RoleGet(role string, r *v3.AuthRoleGetResponse) {
+ resp := (*pb.AuthRoleGetResponse)(r)
fmt.Printf("Role %s\n", role)
- if rootRole == role && r.Perm == nil {
+ if rootRole == role && resp.GetPerm() == nil {
fmt.Println("KV Read:")
fmt.Println("\t[, ")
fmt.Println("KV Write:")
@@ -208,9 +216,9 @@ func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
fmt.Println("KV Read:")
- printRange := func(perm *v3.Permission) {
- sKey := string(perm.Key)
- sRangeEnd := string(perm.RangeEnd)
+ printRange := func(perm *authpb.Permission) {
+ sKey := string(perm.GetKey())
+ sRangeEnd := string(perm.GetRangeEnd())
if sRangeEnd != "\x00" {
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
} else {
@@ -222,42 +230,42 @@ func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
fmt.Print("\n")
}
- for _, perm := range r.Perm {
- if perm.PermType == v3.PermRead || perm.PermType == v3.PermReadWrite {
- if len(perm.RangeEnd) == 0 {
- fmt.Printf("\t%s\n", perm.Key)
+ for _, perm := range resp.GetPerm() {
+ if perm.GetPermType() == v3.PermRead || perm.GetPermType() == v3.PermReadWrite {
+ if len(perm.GetRangeEnd()) == 0 {
+ fmt.Printf("\t%s\n", perm.GetKey())
} else {
- printRange((*v3.Permission)(perm))
+ printRange(perm)
}
}
}
fmt.Println("KV Write:")
- for _, perm := range r.Perm {
- if perm.PermType == v3.PermWrite || perm.PermType == v3.PermReadWrite {
- if len(perm.RangeEnd) == 0 {
- fmt.Printf("\t%s\n", perm.Key)
+ for _, perm := range resp.GetPerm() {
+ if perm.GetPermType() == v3.PermWrite || perm.GetPermType() == v3.PermReadWrite {
+ if len(perm.GetRangeEnd()) == 0 {
+ fmt.Printf("\t%s\n", perm.GetKey())
} else {
- printRange((*v3.Permission)(perm))
+ printRange(perm)
}
}
}
}
-func (s *simplePrinter) RoleList(r v3.AuthRoleListResponse) {
- for _, role := range r.Roles {
+func (s *simplePrinter) RoleList(r *v3.AuthRoleListResponse) {
+ for _, role := range (*pb.AuthRoleListResponse)(r).GetRoles() {
fmt.Printf("%s\n", role)
}
}
-func (s *simplePrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) {
+func (s *simplePrinter) RoleDelete(role string, r *v3.AuthRoleDeleteResponse) {
fmt.Printf("Role %s deleted\n", role)
}
-func (s *simplePrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
+func (s *simplePrinter) RoleGrantPermission(role string, r *v3.AuthRoleGrantPermissionResponse) {
fmt.Printf("Role %s updated\n", role)
}
-func (s *simplePrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
+func (s *simplePrinter) RoleRevokePermission(role string, key string, end string, r *v3.AuthRoleRevokePermissionResponse) {
if len(end) == 0 {
fmt.Printf("Permission of key %s is revoked from role %s\n", key, role)
return
@@ -269,42 +277,43 @@ func (s *simplePrinter) RoleRevokePermission(role string, key string, end string
}
}
-func (s *simplePrinter) UserAdd(name string, r v3.AuthUserAddResponse) {
+func (s *simplePrinter) UserAdd(name string, r *v3.AuthUserAddResponse) {
fmt.Printf("User %s created\n", name)
}
-func (s *simplePrinter) UserGet(name string, r v3.AuthUserGetResponse) {
+func (s *simplePrinter) UserGet(name string, r *v3.AuthUserGetResponse) {
fmt.Printf("User: %s\n", name)
fmt.Print("Roles:")
- for _, role := range r.Roles {
+ for _, role := range (*pb.AuthUserGetResponse)(r).GetRoles() {
fmt.Printf(" %s", role)
}
fmt.Print("\n")
}
-func (s *simplePrinter) UserChangePassword(v3.AuthUserChangePasswordResponse) {
+func (s *simplePrinter) UserChangePassword(*v3.AuthUserChangePasswordResponse) {
fmt.Println("Password updated")
}
-func (s *simplePrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
+func (s *simplePrinter) UserGrantRole(user string, role string, r *v3.AuthUserGrantRoleResponse) {
fmt.Printf("Role %s is granted to user %s\n", role, user)
}
-func (s *simplePrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
+func (s *simplePrinter) UserRevokeRole(user string, role string, r *v3.AuthUserRevokeRoleResponse) {
fmt.Printf("Role %s is revoked from user %s\n", role, user)
}
-func (s *simplePrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) {
+func (s *simplePrinter) UserDelete(user string, r *v3.AuthUserDeleteResponse) {
fmt.Printf("User %s deleted\n", user)
}
-func (s *simplePrinter) UserList(r v3.AuthUserListResponse) {
- for _, user := range r.Users {
+func (s *simplePrinter) UserList(r *v3.AuthUserListResponse) {
+ for _, user := range (*pb.AuthUserListResponse)(r).GetUsers() {
fmt.Printf("%s\n", user)
}
}
-func (s *simplePrinter) AuthStatus(r v3.AuthStatusResponse) {
- fmt.Println("Authentication Status:", r.Enabled)
- fmt.Println("AuthRevision:", r.AuthRevision)
+func (s *simplePrinter) AuthStatus(r *v3.AuthStatusResponse) {
+ resp := (*pb.AuthStatusResponse)(r)
+ fmt.Println("Authentication Status:", resp.GetEnabled())
+ fmt.Println("AuthRevision:", resp.GetAuthRevision())
}
diff --git a/etcdctl/ctlv3/command/printer_table.go b/etcdctl/ctlv3/command/printer_table.go
index c576231ed5b0..f6f72fe35207 100644
--- a/etcdctl/ctlv3/command/printer_table.go
+++ b/etcdctl/ctlv3/command/printer_table.go
@@ -18,52 +18,53 @@ import (
"os"
"github.com/olekukonko/tablewriter"
+ "github.com/olekukonko/tablewriter/tw"
v3 "go.etcd.io/etcd/client/v3"
)
type tablePrinter struct{ printer }
-func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
+func (tp *tablePrinter) MemberList(r *v3.MemberListResponse) {
hdr, rows := makeMemberListTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) EndpointHealth(r []epHealth) {
hdr, rows := makeEndpointHealthTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) EndpointStatus(r []epStatus) {
hdr, rows := makeEndpointStatusTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) EndpointHashKV(r []epHashKV) {
hdr, rows := makeEndpointHashKVTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
diff --git a/etcdctl/ctlv3/command/put_command.go b/etcdctl/ctlv3/command/put_command.go
index 4814db55990c..6e92452d59ae 100644
--- a/etcdctl/ctlv3/command/put_command.go
+++ b/etcdctl/ctlv3/command/put_command.go
@@ -56,7 +56,8 @@ For example,
$ cat file | put
will store the content of the file to .
`,
- Run: putCommandFunc,
+ Run: putCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().StringVar(&leaseStr, "lease", "0", "lease ID (in hexadecimal) to attach to the key")
cmd.Flags().BoolVar(&putPrevKV, "prev-kv", false, "return the previous key-value pair before modification")
@@ -75,7 +76,7 @@ func putCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.Put(*resp)
+ display.Put(resp)
}
func getPutOp(args []string) (string, string, []clientv3.OpOption) {
diff --git a/etcdctl/ctlv3/command/role_command.go b/etcdctl/ctlv3/command/role_command.go
index 705d88d886fa..c63fc6c651a7 100644
--- a/etcdctl/ctlv3/command/role_command.go
+++ b/etcdctl/ctlv3/command/role_command.go
@@ -32,8 +32,10 @@ var (
// NewRoleCommand returns the cobra command for "role".
func NewRoleCommand() *cobra.Command {
ac := &cobra.Command{
- Use: "role ",
- Short: "Role related commands",
+ Use: "role ",
+ Short: "Role related commands. Use `etcdctl role --help` to see subcommands",
+ Long: "Role related commands",
+ GroupID: groupAuthenticationID,
}
ac.AddCommand(newRoleAddCommand())
@@ -115,7 +117,7 @@ func roleAddCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleAdd(args[0], *resp)
+ display.RoleAdd(args[0], resp)
}
// roleDeleteCommandFunc executes the "role delete" command.
@@ -129,7 +131,7 @@ func roleDeleteCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleDelete(args[0], *resp)
+ display.RoleDelete(args[0], resp)
}
// roleGetCommandFunc executes the "role get" command.
@@ -144,7 +146,7 @@ func roleGetCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleGet(name, *resp)
+ display.RoleGet(name, resp)
}
// roleListCommandFunc executes the "role list" command.
@@ -158,7 +160,7 @@ func roleListCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleList(*resp)
+ display.RoleList(resp)
}
// roleGrantPermissionCommandFunc executes the "role grant-permission" command.
@@ -178,7 +180,7 @@ func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleGrantPermission(args[0], *resp)
+ display.RoleGrantPermission(args[0], resp)
}
// roleRevokePermissionCommandFunc executes the "role revoke-permission" command.
@@ -192,7 +194,7 @@ func roleRevokePermissionCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleRevokePermission(args[0], args[1], rangeEnd, *resp)
+ display.RoleRevokePermission(args[0], args[1], rangeEnd, resp)
}
func permRange(args []string) (string, string) {
diff --git a/etcdctl/ctlv3/command/snapshot_command.go b/etcdctl/ctlv3/command/snapshot_command.go
index 66934bede376..dcdab90ee6c0 100644
--- a/etcdctl/ctlv3/command/snapshot_command.go
+++ b/etcdctl/ctlv3/command/snapshot_command.go
@@ -37,7 +37,7 @@ var snapshotExample = util.Normalize(`
# Get snapshot from given address with certificates
etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/etcd/ca.crt --cert=/etc/etcd/etcd.crt --key=/etc/etcd/etcd.key snapshot save /backup/etcd-snapshot.db
- # Get snapshot wih certain user and password
+ # Get snapshot with certain user and password
etcdctl --user=root --password=password123 snapshot save /backup/etcd-snapshot.db
# Get snapshot from given address with timeout
@@ -52,6 +52,7 @@ func NewSnapshotCommand() *cobra.Command {
Use: "snapshot ",
Short: "Manages etcd node snapshots",
Example: snapshotExample,
+ GroupID: groupClusterMaintenanceID,
}
cmd.AddCommand(NewSnapshotSaveCommand())
return cmd
diff --git a/etcdctl/ctlv3/command/txn_command.go b/etcdctl/ctlv3/command/txn_command.go
index 5433f82bda68..bbbf579b1827 100644
--- a/etcdctl/ctlv3/command/txn_command.go
+++ b/etcdctl/ctlv3/command/txn_command.go
@@ -24,7 +24,6 @@ import (
"github.com/spf13/cobra"
- pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
@@ -59,7 +58,8 @@ put key2 "some extra key"
---
Refer to https://github.com/etcd-io/etcd/blob/main/etcdctl/README.md#txn-options.`,
- Run: txnCommandFunc,
+ Run: txnCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().BoolVarP(&txnInteractive, "interactive", "i", false, "Input transaction in interactive mode")
return cmd
@@ -86,7 +86,7 @@ func txnCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.Txn(*resp)
+ display.Txn(resp)
}
func promptInteractive(s string) {
@@ -150,16 +150,19 @@ func parseRequestUnion(line string) (*clientv3.Op, error) {
opc := make(chan clientv3.Op, 1)
put := NewPutCommand()
+ put.GroupID = ""
put.Run = func(cmd *cobra.Command, args []string) {
key, value, opts := getPutOp(args)
opc <- clientv3.OpPut(key, value, opts...)
}
get := NewGetCommand()
+ get.GroupID = ""
get.Run = func(cmd *cobra.Command, args []string) {
key, opts := getGetOp(args)
opc <- clientv3.OpGet(key, opts...)
}
del := NewDelCommand()
+ del.GroupID = ""
del.Run = func(cmd *cobra.Command, args []string) {
key, opts := getDelOp(args)
opc <- clientv3.OpDelete(key, opts...)
@@ -218,7 +221,7 @@ func ParseCompare(line string) (*clientv3.Cmp, error) {
case "val", "value":
cmp = clientv3.Compare(clientv3.Value(key), op, val)
case "lease":
- cmp = clientv3.Compare(clientv3.Cmp{Target: pb.Compare_LEASE}, op, val)
+ cmp = clientv3.Compare(clientv3.LeaseValue(key), op, val)
default:
return nil, fmt.Errorf("malformed comparison: %s (unknown target %s)", line, target)
}
diff --git a/etcdctl/ctlv3/command/user_command.go b/etcdctl/ctlv3/command/user_command.go
index f3e59a7048bb..7de63210048e 100644
--- a/etcdctl/ctlv3/command/user_command.go
+++ b/etcdctl/ctlv3/command/user_command.go
@@ -31,8 +31,10 @@ var userShowDetail bool
// NewUserCommand returns the cobra command for "user".
func NewUserCommand() *cobra.Command {
ac := &cobra.Command{
- Use: "user ",
- Short: "User related commands",
+ Use: "user ",
+ Short: "User related commands. Use `etcdctl user --help` to see subcommands",
+ Long: "User related commands",
+ GroupID: groupAuthenticationID,
}
ac.AddCommand(newUserAddCommand())
@@ -166,7 +168,7 @@ func userAddCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserAdd(user, *resp)
+ display.UserAdd(user, resp)
}
// userDeleteCommandFunc executes the "user delete" command.
@@ -179,7 +181,7 @@ func userDeleteCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserDelete(args[0], *resp)
+ display.UserDelete(args[0], resp)
}
// userGetCommandFunc executes the "user get" command.
@@ -203,10 +205,10 @@ func userGetCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.RoleGet(role, *roleResp)
+ display.RoleGet(role, roleResp)
}
} else {
- display.UserGet(name, *resp)
+ display.UserGet(name, resp)
}
}
@@ -221,7 +223,7 @@ func userListCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserList(*resp)
+ display.UserList(resp)
}
// userChangePasswordCommandFunc executes the "user passwd" command.
@@ -243,7 +245,7 @@ func userChangePasswordCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserChangePassword(*resp)
+ display.UserChangePassword(resp)
}
// userGrantRoleCommandFunc executes the "user grant-role" command.
@@ -257,7 +259,7 @@ func userGrantRoleCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserGrantRole(args[0], args[1], *resp)
+ display.UserGrantRole(args[0], args[1], resp)
}
// userRevokeRoleCommandFunc executes the "user revoke-role" command.
@@ -271,7 +273,7 @@ func userRevokeRoleCommandFunc(cmd *cobra.Command, args []string) {
cobrautl.ExitWithError(cobrautl.ExitError, err)
}
- display.UserRevokeRole(args[0], args[1], *resp)
+ display.UserRevokeRole(args[0], args[1], resp)
}
func readPasswordInteractive(name string) string {
diff --git a/etcdctl/ctlv3/command/util.go b/etcdctl/ctlv3/command/util.go
index 1ee33102bd9a..aa7f784a3f83 100644
--- a/etcdctl/ctlv3/command/util.go
+++ b/etcdctl/ctlv3/command/util.go
@@ -34,10 +34,10 @@ import (
)
func printKV(isHex bool, valueOnly bool, kv *pb.KeyValue) {
- k, v := string(kv.Key), string(kv.Value)
+ k, v := string(kv.GetKey()), string(kv.GetValue())
if isHex {
- k = addHexPrefix(hex.EncodeToString(kv.Key))
- v = addHexPrefix(hex.EncodeToString(kv.Value))
+ k = addHexPrefix(hex.EncodeToString(kv.GetKey()))
+ v = addHexPrefix(hex.EncodeToString(kv.GetValue()))
}
if !valueOnly {
fmt.Println(k)
diff --git a/etcdctl/ctlv3/command/version_command.go b/etcdctl/ctlv3/command/version_command.go
index e57567d6a3d2..d5be27adb056 100644
--- a/etcdctl/ctlv3/command/version_command.go
+++ b/etcdctl/ctlv3/command/version_command.go
@@ -25,9 +25,10 @@ import (
// NewVersionCommand prints out the version of etcd.
func NewVersionCommand() *cobra.Command {
return &cobra.Command{
- Use: "version",
- Short: "Prints the version of etcdctl",
- Run: versionCommandFunc,
+ Use: "version",
+ Short: "Prints the version of etcdctl",
+ Run: versionCommandFunc,
+ GroupID: groupUtilityID,
}
}
diff --git a/etcdctl/ctlv3/command/watch_command.go b/etcdctl/ctlv3/command/watch_command.go
index fa8fc72d82f9..01e4d5d82357 100644
--- a/etcdctl/ctlv3/command/watch_command.go
+++ b/etcdctl/ctlv3/command/watch_command.go
@@ -47,9 +47,10 @@ var (
// NewWatchCommand returns the cobra command for "watch".
func NewWatchCommand() *cobra.Command {
cmd := &cobra.Command{
- Use: "watch [options] [key or prefix] [range_end] [--] [exec-command arg1 arg2 ...]",
- Short: "Watches events stream on keys or prefixes",
- Run: watchCommandFunc,
+ Use: "watch [options] [key or prefix] [range_end] [--] [exec-command arg1 arg2 ...]",
+ Short: "Watches events stream on keys or prefixes",
+ Run: watchCommandFunc,
+ GroupID: groupKVID,
}
cmd.Flags().BoolVarP(&watchInteractive, "interactive", "i", false, "Interactive mode")
@@ -168,18 +169,18 @@ func printWatchCh(c *clientv3.Client, ch clientv3.WatchChan, execArgs []string)
fmt.Fprintf(os.Stderr, "watch was canceled (%v)\n", resp.Err())
}
if resp.IsProgressNotify() {
- fmt.Fprintf(os.Stdout, "progress notify: %d\n", resp.Header.Revision)
+ fmt.Fprintf(os.Stdout, "progress notify: %d\n", resp.Header.GetRevision())
}
- display.Watch(resp)
+ display.Watch(&resp)
if len(execArgs) > 0 {
- for _, ev := range resp.Events {
+ for _, event := range resp.Events {
cmd := exec.CommandContext(c.Ctx(), execArgs[0], execArgs[1:]...)
cmd.Env = os.Environ()
- cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_REVISION=%d", resp.Header.Revision))
- cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_EVENT_TYPE=%q", ev.Type))
- cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_KEY=%q", ev.Kv.Key))
- cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_VALUE=%q", ev.Kv.Value))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_REVISION=%d", resp.Header.GetRevision()))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_EVENT_TYPE=%q", event.GetType()))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_KEY=%q", event.GetKv().GetKey()))
+ cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_VALUE=%q", event.GetKv().GetValue()))
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "command %q error (%v)\n", execArgs, err)
diff --git a/etcdctl/ctlv3/ctl.go b/etcdctl/ctlv3/ctl.go
index 6686940cec7c..039485ddf84d 100644
--- a/etcdctl/ctlv3/ctl.go
+++ b/etcdctl/ctlv3/ctl.go
@@ -16,12 +16,13 @@
package ctlv3
import (
+ "fmt"
"os"
"time"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
- "go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/etcdctl/v3/ctlv3/command"
"go.etcd.io/etcd/pkg/v3/cobrautl"
)
@@ -69,11 +70,20 @@ func init() {
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CertFile, "cert", "", "identify secure client using this TLS certificate file")
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.KeyFile, "key", "", "identify secure client using this TLS key file")
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.TrustedCAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle")
+ rootCmd.PersistentFlags().StringVar(&globalFlags.Token, "auth-jwt-token", "", "JWT token used for authentication (if this option is used, --user and --password should not be set)")
rootCmd.PersistentFlags().StringVar(&globalFlags.User, "user", "", "username[:password] for authentication (prompt if password is not supplied)")
rootCmd.PersistentFlags().StringVar(&globalFlags.Password, "password", "", "password for authentication (if this option is used, --user option shouldn't include password)")
rootCmd.PersistentFlags().StringVarP(&globalFlags.TLS.ServerName, "discovery-srv", "d", "", "domain name to query for SRV records describing cluster endpoints")
rootCmd.PersistentFlags().StringVarP(&globalFlags.DNSClusterServiceName, "discovery-srv-name", "", "", "service name to query when using DNS discovery")
+ rootCmd.AddGroup(
+ command.NewKVGroup(),
+ command.NewClusterMaintenanceGroup(),
+ command.NewConcurrencyGroup(),
+ command.NewAuthenticationGroup(),
+ command.NewUtilityGroup(),
+ )
+
rootCmd.AddCommand(
command.NewGetCommand(),
command.NewPutCommand(),
@@ -98,17 +108,16 @@ func init() {
command.NewCheckCommand(),
command.NewCompletionCommand(),
command.NewDowngradeCommand(),
+ command.NewOptionsCommand(rootCmd),
)
-}
+ command.SetHelpCmdGroup(rootCmd)
-func usageFunc(c *cobra.Command) error {
- return cobrautl.UsageFunc(c, version.Version, version.APIVersion)
+ hideAllGlobalFlags()
+ hideHelpFlag()
+ addOptionsPrompt()
}
func Start() error {
- rootCmd.SetUsageFunc(usageFunc)
- // Make help just show the usage
- rootCmd.SetHelpTemplate(`{{.UsageString}}`)
return rootCmd.Execute()
}
@@ -121,6 +130,27 @@ func MustStart() {
}
}
+func hideAllGlobalFlags() {
+ rootCmd.PersistentFlags().VisitAll(func(f *pflag.Flag) {
+ rootCmd.PersistentFlags().MarkHidden(f.Name)
+ })
+}
+
+func hideHelpFlag() {
+ if rootCmd.Flags().Lookup("help") == nil {
+ rootCmd.Flags().BoolP("help", "h", false, "help for "+rootCmd.Name())
+ }
+ rootCmd.Flags().MarkHidden("help")
+}
+
+func addOptionsPrompt() {
+ defaultHelpFunc := rootCmd.HelpFunc()
+ rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
+ defaultHelpFunc(cmd, args)
+ fmt.Fprintln(cmd.OutOrStdout(), `Use "etcdctl options" for a list of global command-line options (applies to all commands).`)
+ })
+}
+
func init() {
cobra.EnablePrefixMatching = true
}
diff --git a/etcdctl/go.mod b/etcdctl/go.mod
index f32d73bb7d58..0e1a3b0460de 100644
--- a/etcdctl/go.mod
+++ b/etcdctl/go.mod
@@ -1,48 +1,52 @@
module go.etcd.io/etcd/etcdctl/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/bgentry/speakeasy v0.2.0
- github.com/cheggaaa/pb/v3 v3.1.6
+ github.com/cheggaaa/pb/v3 v3.1.7
github.com/dustin/go-humanize v1.0.1
- github.com/olekukonko/tablewriter v0.0.5
+ github.com/olekukonko/tablewriter v1.1.4
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.etcd.io/etcd/client/v3 v3.6.10
- go.etcd.io/etcd/pkg/v3 v3.6.10
- go.uber.org/zap v1.27.0
- golang.org/x/time v0.9.0
- google.golang.org/grpc v1.79.3
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ go.etcd.io/etcd/pkg/v3 v3.7.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/time v0.15.0
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
)
require (
github.com/VividCortex/ewma v1.2.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/clipperhouse/displaywidth v0.11.0 // indirect
+ github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/fatih/color v1.18.0 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/fatih/color v1.19.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/mattn/go-runewidth v0.0.23 // indirect
+ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
+ github.com/olekukonko/errors v1.2.0 // indirect
+ github.com/olekukonko/ll v0.1.6 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/protobuf v1.36.10 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -52,11 +56,3 @@ replace (
go.etcd.io/etcd/client/v3 => ../client/v3
go.etcd.io/etcd/pkg/v3 => ../pkg
)
-
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-replace (
- go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/tests/v3 => ./FORBIDDEN_DEPENDENCY
-)
diff --git a/etcdctl/go.sum b/etcdctl/go.sum
index a6b2a7600d1c..3b570a84763d 100644
--- a/etcdctl/go.sum
+++ b/etcdctl/go.sum
@@ -6,44 +6,41 @@ github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE5
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
-github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
+github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
+github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
+github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -52,26 +49,28 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
+github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
+github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
+github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
+github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
+github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
+github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
+github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -82,71 +81,46 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/etcdutl/.gomodguard.yaml b/etcdutl/.gomodguard.yaml
new file mode 100644
index 000000000000..18ffc6800c1f
--- /dev/null
+++ b/etcdutl/.gomodguard.yaml
@@ -0,0 +1,9 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/etcdutl/LICENSE b/etcdutl/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/etcdutl/LICENSE
+++ b/etcdutl/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/etcdutl/README.md b/etcdutl/README.md
index 06cf85d7e9b1..ead1b41ccd33 100644
--- a/etcdutl/README.md
+++ b/etcdutl/README.md
@@ -1,5 +1,4 @@
-etcdutl
-========
+# etcdutl
`etcdutl` is a command line administration utility for [etcd][etcd].
@@ -8,7 +7,7 @@ For operations over a network, please use `etcdctl`.
### DEFRAG [options]
-DEFRAG directly defragments an etcd data directory while etcd is not running.
+DEFRAG directly defragments an etcd data directory while etcd is not running.
When an etcd member reclaims storage space from deleted and compacted keys, the space is kept in a free list and the database file remains the same size. By defragmenting the database, the etcd member releases this free space back to the file system.
In order to defrag a live etcd instances over the network, please use `etcdctl defrag` instead.
@@ -36,7 +35,6 @@ To defragment a data directory directly, use the `--data-dir` flag:
DEFRAG returns a zero exit code only if it succeeded in defragmenting all given endpoints.
-
### SNAPSHOT RESTORE [options] \
SNAPSHOT RESTORE creates an etcd data directory for an etcd cluster member from a backend database snapshot and a new cluster configuration. Restoring the snapshot into each member for a new cluster configuration will initialize a new etcd cluster preloaded by the snapshot data.
@@ -174,6 +172,74 @@ Prints etcd version and API version.
# API version: 3.1
```
+### LIST-BUCKET [options] \
+
+`list-bucket` prints all bucket names.
+
+#### Flags
+
+- timeout -- Time to wait to obtain a file lock on db file, 0 to block indefinitely.
+
+##### Examples for LIST-BUCKET
+
+```bash
+
+$ ./etcdutl list-bucket ~/tmp/etcd/default.etcd/member/snap/db
+alarm
+auth
+authRoles
+authUsers
+cluster
+key
+lease
+members
+members_removed
+meta
+```
+
+### ITERATE-BUCKET [options] \ \
+
+`iterate-bucket` lists key-value pairs in a given bucket in reverse order.
+
+#### Flags for ITERATE-BUCKET
+
+- timeout -- Time to wait to obtain a file lock on db file, 0 to block indefinitely.
+- limit -- Max number of key-value pairs to iterate (0 to iterate all).
+- decode -- true to decode Protocol Buffer encoded data.
+
+##### Examples for ITERATE-BUCKET
+
+```bash
+
+# with `--decode` option
+$ ./etcdutl iterate-bucket ~/tmp/etcd/default.etcd/member/snap/db key --decode
+rev={Revision:{Main:4 Sub:0} tombstone:false}, value=[key "k1" | val "v3" | created 2 | mod 4 | ver 3]
+rev={Revision:{Main:3 Sub:0} tombstone:false}, value=[key "k1" | val "v2" | created 2 | mod 3 | ver 2]
+rev={Revision:{Main:2 Sub:0} tombstone:false}, value=[key "k1" | val "v1" | created 2 | mod 2 | ver 1]
+
+# without `--decode` option
+$ ./etcdutl iterate-bucket ~/tmp/etcd/default.etcd/member/snap/db key
+key="\x00\x00\x00\x00\x00\x00\x00\x04_\x00\x00\x00\x00\x00\x00\x00\x00", value="\n\x02k1\x10\x02\x18\x04 \x03*\x02v3"
+key="\x00\x00\x00\x00\x00\x00\x00\x03_\x00\x00\x00\x00\x00\x00\x00\x00", value="\n\x02k1\x10\x02\x18\x03 \x02*\x02v2"
+key="\x00\x00\x00\x00\x00\x00\x00\x02_\x00\x00\x00\x00\x00\x00\x00\x00", value="\n\x02k1\x10\x02\x18\x02 \x01*\x02v1"
+```
+
+### HASH [options] \
+
+`hash` prints the hash of the db file.
+
+#### Flags for HASH
+
+- timeout -- Time to wait to obtain a file lock on db file, 0 to block indefinitely.
+
+##### Examples for HASH
+
+```bash
+
+$ ./etcdutl hash ~/tmp/etcd/default.etcd/member/snap/db
+db path: /Users/wachao/tmp/etcd/default.etcd/member/snap/db
+Hash: 4031086527
+```
## Exit codes
diff --git a/etcdutl/ctl.go b/etcdutl/ctl.go
index 8418729bfd1a..02793a64d366 100644
--- a/etcdutl/ctl.go
+++ b/etcdutl/ctl.go
@@ -16,6 +16,8 @@
package main
import (
+ "time"
+
"github.com/spf13/cobra"
"go.etcd.io/etcd/etcdutl/v3/etcdutl"
@@ -33,6 +35,7 @@ var rootCmd = &cobra.Command{
}
func init() {
+ rootCmd.PersistentFlags().DurationVar(&etcdutl.FlockTimeout, "timeout", 10*time.Second, "time to wait to obtain a file lock on db file, 0 to block indefinitely")
rootCmd.PersistentFlags().StringVarP(&etcdutl.OutputFormat, "write-out", "w", "simple", "set the output format (fields, json, protobuf, simple, table)")
rootCmd.RegisterFlagCompletionFunc("write-out", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"fields", "json", "protobuf", "simple", "table"}, cobra.ShellCompDirectiveDefault
diff --git a/etcdutl/etcdutl/common.go b/etcdutl/etcdutl/common.go
index c7473cc7fd7b..e6b09efb7015 100644
--- a/etcdutl/etcdutl/common.go
+++ b/etcdutl/etcdutl/common.go
@@ -15,20 +15,37 @@
package etcdutl
import (
- "errors"
+ "os"
+ "time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/pkg/v3/cobrautl"
- "go.etcd.io/etcd/server/v3/etcdserver/api/snap"
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/datadir"
"go.etcd.io/etcd/server/v3/storage/wal"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
- "go.etcd.io/raft/v3/raftpb"
)
+// validateDataDir checks if the data directory's backend database file exists.
+func validateDataDir(dataDir string) error {
+ dbPath := datadir.ToBackendFileName(dataDir)
+ return validateFilePath(dbPath)
+}
+
+// validateFilePath checks if the specified file exists.
+// It returns an error encountered by os.Stat, such as os.ErrNotExist, os.ErrPermission, etc.
+func validateFilePath(filePath string) error {
+ _, err := os.Stat(filePath)
+ return err
+}
+
+// FlockTimeout is the duration to wait to obtain a file lock on db file.
+var FlockTimeout time.Duration
+
func GetLogger() *zap.Logger {
config := logutil.DefaultZapLoggerConfig
config.Encoding = "console"
@@ -40,31 +57,63 @@ func GetLogger() *zap.Logger {
return lg
}
-func getLatestWALSnap(lg *zap.Logger, dataDir string) (walpb.Snapshot, error) {
- snapshot, err := getLatestV2Snapshot(lg, dataDir)
- if err != nil {
- return walpb.Snapshot{}, err
- }
-
- var walsnap walpb.Snapshot
- if snapshot != nil {
- walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
- }
- return walsnap, nil
-}
-
-func getLatestV2Snapshot(lg *zap.Logger, dataDir string) (*raftpb.Snapshot, error) {
+func getLatestWALSnap(lg *zap.Logger, dataDir string) (*walpb.Snapshot, error) {
walPath := datadir.ToWALDir(dataDir)
walSnaps, err := wal.ValidSnapshotEntries(lg, walPath)
if err != nil {
return nil, err
}
- ss := snap.New(lg, datadir.ToSnapDir(dataDir))
- snapshot, err := ss.LoadNewestAvailable(walSnaps)
- if err != nil && !errors.Is(err, snap.ErrNoSnapshot) {
- return nil, err
+ if len(walSnaps) > 0 {
+ lastIdx := len(walSnaps) - 1
+ return walSnaps[lastIdx], nil
}
+ return &walpb.Snapshot{}, nil
+}
+
+// SimpleLessor is a simplified implementation of Lessor interface.
+// Used by etcdutl tools to simulate Lessor behavior without full lease management
+type SimpleLessor struct {
+ LeaseSet map[lease.LeaseID]struct{}
+}
+
+var _ lease.Lessor = (*SimpleLessor)(nil)
- return snapshot, nil
+func (sl *SimpleLessor) SetRangeDeleter(dr lease.RangeDeleter) {}
+
+func (sl *SimpleLessor) SetCheckpointer(cp lease.Checkpointer) {}
+
+func (sl *SimpleLessor) Grant(id lease.LeaseID, ttl int64) (*lease.Lease, error) {
+ sl.LeaseSet[id] = struct{}{}
+ return nil, nil
+}
+
+func (sl *SimpleLessor) Revoke(id lease.LeaseID) error { return nil }
+
+func (sl *SimpleLessor) Checkpoint(id lease.LeaseID, remainingTTL int64) error { return nil }
+
+func (sl *SimpleLessor) Attach(id lease.LeaseID, items []lease.LeaseItem) error { return nil }
+
+func (sl *SimpleLessor) GetLease(item lease.LeaseItem) lease.LeaseID { return 0 }
+func (sl *SimpleLessor) Detach(id lease.LeaseID, items []lease.LeaseItem) error { return nil }
+
+func (sl *SimpleLessor) Promote(extend time.Duration) {}
+
+func (sl *SimpleLessor) Demote() {}
+
+func (sl *SimpleLessor) Renew(id lease.LeaseID) (int64, error) { return 10, nil }
+
+func (sl *SimpleLessor) Lookup(id lease.LeaseID) *lease.Lease {
+ if _, ok := sl.LeaseSet[id]; ok {
+ return &lease.Lease{ID: id}
+ }
+ return nil
}
+
+func (sl *SimpleLessor) Leases() []*lease.Lease { return nil }
+
+func (sl *SimpleLessor) ExpiredLeasesC() <-chan []*lease.Lease { return nil }
+
+func (sl *SimpleLessor) Recover(b backend.Backend, rd lease.RangeDeleter) {}
+
+func (sl *SimpleLessor) Stop() {}
diff --git a/etcdutl/etcdutl/common_test.go b/etcdutl/etcdutl/common_test.go
index e2acedfa2e73..6bf10467fb6c 100644
--- a/etcdutl/etcdutl/common_test.go
+++ b/etcdutl/etcdutl/common_test.go
@@ -33,68 +33,39 @@ import (
func TestGetLatestWalSnap(t *testing.T) {
testCases := []struct {
name string
- walSnaps []walpb.Snapshot
- snapshots []raftpb.Snapshot
- expectedLatestWALSnap walpb.Snapshot
+ walSnaps []*walpb.Snapshot
+ snapshots []*raftpb.Snapshot
+ expectedLatestWALSnap *walpb.Snapshot
}{
{
name: "wal snapshot records match the snapshot files",
- walSnaps: []walpb.Snapshot{
- {Index: 10, Term: 2},
- {Index: 20, Term: 3},
- {Index: 30, Term: 5},
+ walSnaps: []*walpb.Snapshot{
+ {Index: new(uint64(10)), Term: new(uint64(2))},
+ {Index: new(uint64(20)), Term: new(uint64(3))},
+ {Index: new(uint64(30)), Term: new(uint64(5))},
},
- snapshots: []raftpb.Snapshot{
- {Metadata: raftpb.SnapshotMetadata{Index: 10, Term: 2}},
- {Metadata: raftpb.SnapshotMetadata{Index: 20, Term: 3}},
- {Metadata: raftpb.SnapshotMetadata{Index: 30, Term: 5}},
+ snapshots: []*raftpb.Snapshot{
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(10)), Term: new(uint64(2))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(20)), Term: new(uint64(3))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(30)), Term: new(uint64(5))}},
},
- expectedLatestWALSnap: walpb.Snapshot{Index: 30, Term: 5},
+ expectedLatestWALSnap: &walpb.Snapshot{Index: new(uint64(30)), Term: new(uint64(5))},
},
{
name: "there are orphan snapshot files",
- walSnaps: []walpb.Snapshot{
- {Index: 10, Term: 2},
- {Index: 20, Term: 3},
- {Index: 35, Term: 5},
+ walSnaps: []*walpb.Snapshot{
+ {Index: new(uint64(10)), Term: new(uint64(2))},
+ {Index: new(uint64(20)), Term: new(uint64(3))},
+ {Index: new(uint64(35)), Term: new(uint64(5))},
},
- snapshots: []raftpb.Snapshot{
- {Metadata: raftpb.SnapshotMetadata{Index: 10, Term: 2}},
- {Metadata: raftpb.SnapshotMetadata{Index: 20, Term: 3}},
- {Metadata: raftpb.SnapshotMetadata{Index: 35, Term: 5}},
- {Metadata: raftpb.SnapshotMetadata{Index: 40, Term: 6}},
- {Metadata: raftpb.SnapshotMetadata{Index: 50, Term: 7}},
+ snapshots: []*raftpb.Snapshot{
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(10)), Term: new(uint64(2))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(20)), Term: new(uint64(3))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(35)), Term: new(uint64(5))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(40)), Term: new(uint64(6))}},
+ {Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(50)), Term: new(uint64(7))}},
},
- expectedLatestWALSnap: walpb.Snapshot{Index: 35, Term: 5},
- },
- {
- name: "there are orphan snapshot records in wal file",
- walSnaps: []walpb.Snapshot{
- {Index: 10, Term: 2},
- {Index: 20, Term: 3},
- {Index: 30, Term: 4},
- {Index: 45, Term: 5},
- {Index: 55, Term: 6},
- },
- snapshots: []raftpb.Snapshot{
- {Metadata: raftpb.SnapshotMetadata{Index: 10, Term: 2}},
- {Metadata: raftpb.SnapshotMetadata{Index: 20, Term: 3}},
- {Metadata: raftpb.SnapshotMetadata{Index: 30, Term: 4}},
- },
- expectedLatestWALSnap: walpb.Snapshot{Index: 30, Term: 4},
- },
- {
- name: "wal snapshot records do not match the snapshot files at all",
- walSnaps: []walpb.Snapshot{
- {Index: 10, Term: 2},
- {Index: 20, Term: 3},
- {Index: 30, Term: 4},
- },
- snapshots: []raftpb.Snapshot{
- {Metadata: raftpb.SnapshotMetadata{Index: 40, Term: 5}},
- {Metadata: raftpb.SnapshotMetadata{Index: 50, Term: 6}},
- },
- expectedLatestWALSnap: walpb.Snapshot{},
+ expectedLatestWALSnap: &walpb.Snapshot{Index: new(uint64(35)), Term: new(uint64(5))},
},
}
@@ -108,10 +79,10 @@ func TestGetLatestWalSnap(t *testing.T) {
require.NoError(t, fileutil.TouchDirAll(lg, datadir.ToSnapDir(dataDir)))
// populate wal file
- w, err := wal.Create(lg, datadir.ToWALDir(dataDir), pbutil.MustMarshal(
+ w, err := wal.Create(lg, datadir.ToWALDir(dataDir), pbutil.MustMarshalMessage(
&etcdserverpb.Metadata{
- NodeID: 1,
- ClusterID: 2,
+ NodeID: new(uint64(1)),
+ ClusterID: new(uint64(2)),
},
))
require.NoError(t, err)
@@ -120,7 +91,7 @@ func TestGetLatestWalSnap(t *testing.T) {
walSnap.ConfState = &raftpb.ConfState{Voters: []uint64{1}}
walErr := w.SaveSnapshot(walSnap)
require.NoError(t, walErr)
- walErr = w.Save(raftpb.HardState{Term: walSnap.Term, Commit: walSnap.Index, Vote: 1}, nil)
+ walErr = w.Save(&raftpb.HardState{Term: new(walSnap.GetTerm()), Commit: new(walSnap.GetIndex()), Vote: new(uint64(1))}, nil)
require.NoError(t, walErr)
}
err = w.Close()
@@ -129,7 +100,7 @@ func TestGetLatestWalSnap(t *testing.T) {
// generate snapshot files
ss := snap.New(lg, datadir.ToSnapDir(dataDir))
for _, snap := range tc.snapshots {
- snap.Metadata.ConfState = raftpb.ConfState{Voters: []uint64{1}}
+ snap.Metadata.ConfState = &raftpb.ConfState{Voters: []uint64{1}}
snapErr := ss.SaveSnap(snap)
require.NoError(t, snapErr)
}
@@ -137,7 +108,8 @@ func TestGetLatestWalSnap(t *testing.T) {
walSnap, err := getLatestWALSnap(lg, dataDir)
require.NoError(t, err)
- require.Equal(t, tc.expectedLatestWALSnap, walSnap)
+ require.Equal(t, tc.expectedLatestWALSnap.GetTerm(), walSnap.GetTerm())
+ require.Equal(t, tc.expectedLatestWALSnap.GetIndex(), walSnap.GetIndex())
})
}
}
diff --git a/etcdutl/etcdutl/defrag_command.go b/etcdutl/etcdutl/defrag_command.go
index 5135f5f5eb85..5565e79c6adf 100644
--- a/etcdutl/etcdutl/defrag_command.go
+++ b/etcdutl/etcdutl/defrag_command.go
@@ -16,8 +16,6 @@ package etcdutl
import (
"fmt"
- "os"
- "time"
"github.com/spf13/cobra"
@@ -50,23 +48,14 @@ func defragCommandFunc(cmd *cobra.Command, args []string) {
}
func DefragData(dataDir string) error {
- var be backend.Backend
- lg := GetLogger()
- bch := make(chan struct{})
- dbDir := datadir.ToBackendFileName(dataDir)
- go func() {
- defer close(bch)
- cfg := backend.DefaultBackendConfig(lg)
- cfg.Logger = lg
- cfg.Path = dbDir
- be = backend.New(cfg)
- }()
- select {
- case <-bch:
- case <-time.After(time.Second):
- fmt.Fprintf(os.Stderr, "waiting for etcd to close and release its lock on %q. "+
- "To defrag a running etcd instance, use `etcdctl defrag` instead.\n", dbDir)
- <-bch
+ if err := validateDataDir(dataDir); err != nil {
+ return err
}
- return be.Defrag()
+ b := backend.NewDefaultBackend(
+ GetLogger(),
+ datadir.ToBackendFileName(dataDir),
+ backend.WithTimeout(FlockTimeout))
+ defer b.Close()
+
+ return b.Defrag()
}
diff --git a/etcdutl/etcdutl/hashkv_command.go b/etcdutl/etcdutl/hashkv_command.go
index 20c47a514521..c80693ab56f0 100644
--- a/etcdutl/etcdutl/hashkv_command.go
+++ b/etcdutl/etcdutl/hashkv_command.go
@@ -54,10 +54,14 @@ type HashKV struct {
}
func calculateHashKV(dbPath string, rev int64) (HashKV, error) {
- cfg := backend.DefaultBackendConfig(zap.NewNop())
- cfg.Path = dbPath
- b := backend.New(cfg)
- st := mvcc.NewStore(zap.NewNop(), b, nil, mvcc.StoreConfig{})
+ if err := validateFilePath(dbPath); err != nil {
+ return HashKV{}, err
+ }
+ b := backend.NewDefaultBackend(zap.NewNop(), dbPath, backend.WithTimeout(FlockTimeout))
+ defer b.Close()
+ // Since `etcdutl hashkv` only hashes the keyspace and ignores leases, we use a simple lessor to simplify the implementation.
+ st := mvcc.NewStore(zap.NewNop(), b, &SimpleLessor{}, mvcc.StoreConfig{})
+ defer st.Close()
hst := mvcc.NewHashStorage(zap.NewNop(), st)
h, _, err := hst.HashByRev(rev)
diff --git a/etcdutl/etcdutl/hashkv_command_test.go b/etcdutl/etcdutl/hashkv_command_test.go
new file mode 100644
index 000000000000..e3f101e155c1
--- /dev/null
+++ b/etcdutl/etcdutl/hashkv_command_test.go
@@ -0,0 +1,145 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 etcdutl
+
+import (
+ "path/filepath"
+ "testing"
+
+ "go.uber.org/zap"
+ "gotest.tools/v3/assert"
+
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/backend"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+func TestCalculateHashKV(t *testing.T) {
+ type testCase struct {
+ name string
+ setupFunc func(t *testing.T) (dbPath string, cleanup func())
+ revision int64
+ expectedHash uint32
+ expectedRev int64
+ expectedCompact int64
+ expectError bool
+ errorContains string
+ }
+
+ testCases := []testCase{
+ {
+ name: "non-existent file",
+ setupFunc: func(t *testing.T) (string, func()) {
+ return "/nonexistent/path/to/db", func() {}
+ },
+ expectError: true,
+ },
+ {
+ name: "empty directory path",
+ setupFunc: func(t *testing.T) (string, func()) {
+ return "", func() {}
+ },
+ expectError: true,
+ },
+ {
+ name: "empty database",
+ setupFunc: func(t *testing.T) (string, func()) {
+ tempDir := t.TempDir()
+ dbPath := filepath.Join(tempDir, "test.db")
+
+ b := backend.NewDefaultBackend(zap.NewNop(), dbPath)
+ st := mvcc.NewStore(zap.NewNop(), b, &lease.FakeLessor{}, mvcc.StoreConfig{})
+ _ = st
+ b.Close()
+
+ return dbPath, func() {}
+ },
+ revision: 0,
+ expectedHash: 1084519789,
+ expectedRev: 1,
+ expectedCompact: -1,
+ expectError: false,
+ },
+ {
+ name: "database with data",
+ setupFunc: func(t *testing.T) (string, func()) {
+ tempDir := t.TempDir()
+ dbPath := filepath.Join(tempDir, "test_with_data.db")
+
+ b := backend.NewDefaultBackend(zap.NewNop(), dbPath)
+ st := mvcc.NewStore(zap.NewNop(), b, &lease.FakeLessor{}, mvcc.StoreConfig{})
+ st.Put([]byte("test-key"), []byte("test-value"), 1)
+ st.Close()
+ b.Close()
+
+ return dbPath, func() {}
+ },
+ revision: 0,
+ expectedHash: 645561629,
+ expectedRev: 2,
+ expectedCompact: -1,
+ expectError: false,
+ },
+ {
+ name: "invalid revision",
+ setupFunc: func(t *testing.T) (string, func()) {
+ tempDir := t.TempDir()
+ dbPath := filepath.Join(tempDir, "test_invalid_rev.db")
+
+ b := backend.NewDefaultBackend(zap.NewNop(), dbPath)
+ st := mvcc.NewStore(zap.NewNop(), b, &lease.FakeLessor{}, mvcc.StoreConfig{})
+ st.Put([]byte("key"), []byte("value"), 1)
+ st.Close()
+ b.Close()
+
+ return dbPath, func() {}
+ },
+ revision: 999,
+ expectError: true,
+ errorContains: "required revision is a future revision",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Logf("Recovered from panic: %v", r)
+ }
+ }()
+
+ dbPath, cleanup := tc.setupFunc(t)
+ defer cleanup()
+
+ result, err := calculateHashKV(dbPath, tc.revision)
+
+ if tc.expectError {
+ assert.Assert(t, err != nil)
+ if tc.errorContains != "" {
+ assert.ErrorContains(t, err, tc.errorContains)
+ }
+ return
+ }
+
+ assert.NilError(t, err)
+ assert.Equal(t, tc.expectedHash, result.Hash)
+ assert.Equal(t, tc.expectedRev, result.HashRevision)
+ assert.Equal(t, tc.expectedCompact, result.CompactRevision)
+
+ t.Logf("Test %s - Hash: %d, HashRevision: %d, CompactRevision: %d",
+ tc.name, result.Hash, result.HashRevision, result.CompactRevision)
+ })
+ }
+}
diff --git a/etcdutl/etcdutl/migrate_command.go b/etcdutl/etcdutl/migrate_command.go
index a7f3d849f168..4fe5fee90241 100644
--- a/etcdutl/etcdutl/migrate_command.go
+++ b/etcdutl/etcdutl/migrate_command.go
@@ -122,8 +122,10 @@ func (c *migrateConfig) finalize() error {
}
func migrateCommandFunc(c *migrateConfig) error {
- dbPath := datadir.ToBackendFileName(c.dataDir)
- be := backend.NewDefaultBackend(GetLogger(), dbPath)
+ if err := validateDataDir(c.dataDir); err != nil {
+ return err
+ }
+ be := backend.NewDefaultBackend(GetLogger(), datadir.ToBackendFileName(c.dataDir), backend.WithTimeout(FlockTimeout))
defer be.Close()
tx := be.BatchTx()
diff --git a/etcdutl/etcdutl/printer_table.go b/etcdutl/etcdutl/printer_table.go
index ec66ea38f767..8558d5a0bea2 100644
--- a/etcdutl/etcdutl/printer_table.go
+++ b/etcdutl/etcdutl/printer_table.go
@@ -18,6 +18,7 @@ import (
"os"
"github.com/olekukonko/tablewriter"
+ "github.com/olekukonko/tablewriter/tw"
"go.etcd.io/etcd/etcdutl/v3/snapshot"
)
@@ -26,22 +27,22 @@ type tablePrinter struct{ printer }
func (tp *tablePrinter) DBStatus(r snapshot.Status) {
hdr, rows := makeDBStatusTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
func (tp *tablePrinter) DBHashKV(r HashKV) {
hdr, rows := makeDBHashKVTable(r)
- table := tablewriter.NewWriter(os.Stdout)
- table.SetHeader(hdr)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(os.Stdout, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
for _, row := range rows {
table.Append(row)
}
- table.SetAlignment(tablewriter.ALIGN_RIGHT)
table.Render()
}
diff --git a/etcdutl/etcdutl/snapshot_command.go b/etcdutl/etcdutl/snapshot_command.go
index 52cb4a1bbdda..e0d4b07329e5 100644
--- a/etcdutl/etcdutl/snapshot_command.go
+++ b/etcdutl/etcdutl/snapshot_command.go
@@ -94,6 +94,9 @@ func SnapshotStatusCommandFunc(cmd *cobra.Command, args []string) {
err := fmt.Errorf("snapshot status requires exactly one argument")
cobrautl.ExitWithError(cobrautl.ExitBadArgs, err)
}
+ if err := validateFilePath(args[0]); err != nil {
+ cobrautl.ExitWithError(cobrautl.ExitError, err)
+ }
printer := initPrinterFromCmd(cmd)
lg := GetLogger()
diff --git a/etcdutl/go.mod b/etcdutl/go.mod
index dc6e9e4a99e6..67a7ba46807c 100644
--- a/etcdutl/go.mod
+++ b/etcdutl/go.mod
@@ -1,8 +1,8 @@
module go.etcd.io/etcd/etcdutl/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
replace (
go.etcd.io/etcd/api/v3 => ../api
@@ -12,85 +12,83 @@ replace (
go.etcd.io/etcd/server/v3 => ../server
)
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-replace (
- go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/tests/v3 => ./FORBIDDEN_DEPENDENCY
-)
-
require (
github.com/coreos/go-semver v0.3.1
github.com/dustin/go-humanize v1.0.1
- github.com/olekukonko/tablewriter v0.0.5
+ github.com/olekukonko/tablewriter v1.1.4
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
- go.etcd.io/bbolt v1.4.3
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.etcd.io/etcd/client/v3 v3.6.10
- go.etcd.io/etcd/pkg/v3 v3.6.10
- go.etcd.io/etcd/server/v3 v3.6.10
- go.etcd.io/raft/v3 v3.6.0
- go.uber.org/zap v1.27.0
+ go.etcd.io/bbolt v1.5.0
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ go.etcd.io/etcd/pkg/v3 v3.7.0
+ go.etcd.io/etcd/server/v3 v3.7.0
+ go.etcd.io/raft/v3 v3.7.0
+ go.uber.org/zap v1.27.1
+ google.golang.org/protobuf v1.36.11
+ gotest.tools/v3 v3.5.2
)
require (
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/clipperhouse/displaywidth v0.11.0 // indirect
+ github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/fatih/color v1.19.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
+ github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_golang v1.20.5 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.62.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
+ github.com/olekukonko/errors v1.2.0 // indirect
+ github.com/olekukonko/ll v0.1.6 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/client_golang v1.23.2 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/sirupsen/logrus v1.9.4 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
- github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
+ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
- go.opentelemetry.io/otel v1.40.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
- go.opentelemetry.io/otel/metric v1.40.0 // indirect
- go.opentelemetry.io/otel/sdk v1.40.0 // indirect
- go.opentelemetry.io/otel/trace v1.40.0 // indirect
- go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
+ go.opentelemetry.io/otel v1.43.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
+ go.opentelemetry.io/otel/metric v1.43.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.43.0 // indirect
+ go.opentelemetry.io/otel/trace v1.43.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/crypto v0.48.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- golang.org/x/time v0.9.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/grpc v1.79.3 // indirect
- google.golang.org/protobuf v1.36.10 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/crypto v0.52.0 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ golang.org/x/time v0.15.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/grpc v1.81.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
- sigs.k8s.io/yaml v1.4.0 // indirect
+ k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/etcdutl/go.sum b/etcdutl/go.sum
index 41ea9e662948..d2ec3d51af8a 100644
--- a/etcdutl/go.sum
+++ b/etcdutl/go.sum
@@ -1,87 +1,95 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
+github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
+github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
+github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
+github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
+github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
+github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
+github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
+github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
+github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
@@ -90,95 +98,88 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
-go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
-go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
+go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
+go.etcd.io/raft/v3 v3.7.0 h1:BGzlwx07bLv8PW6OU5HObuz1y4hlPZUXA07pM1mPUh4=
+go.etcd.io/raft/v3 v3.7.0/go.mod h1:6gX6T2X907DjnjsFLODnTxba77stjs84W9gTTI0GUNA=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
-go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
-go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
-go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
-go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
-go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
-go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
-go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
-go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
-go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
-go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
-go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
+go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -187,7 +188,9 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
+gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/etcdutl/snapshot/v3_snapshot.go b/etcdutl/snapshot/v3_snapshot.go
index 81dde9a0d0e0..d03286ec92ea 100644
--- a/etcdutl/snapshot/v3_snapshot.go
+++ b/etcdutl/snapshot/v3_snapshot.go
@@ -28,6 +28,7 @@ import (
"strings"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
bolt "go.etcd.io/bbolt"
"go.etcd.io/etcd/api/v3/etcdserverpb"
@@ -172,7 +173,7 @@ func (s *v3Manager) Status(dbPath string) (ds Status, err error) {
ds.Revision = rev.Main
var kv mvccpb.KeyValue
- err = kv.Unmarshal(v)
+ err = proto.Unmarshal(v, &kv)
if err != nil {
return fmt.Errorf("cannot unmarshal value, key: %q value: %q err: %w", k, v, err)
}
@@ -326,7 +327,7 @@ func (s *v3Manager) Restore(cfg RestoreConfig) error {
return err
}
- if err := s.updateCIndex(hardstate.Commit, hardstate.Term); err != nil {
+ if err := s.updateCIndex(hardstate.GetCommit(), hardstate.GetTerm()); err != nil {
return err
}
@@ -519,9 +520,9 @@ func (s *v3Manager) saveWALAndSnap() (*raftpb.HardState, error) {
s.cl.AddMember(m, true)
}
- m := s.cl.MemberByName(s.name)
- md := &etcdserverpb.Metadata{NodeID: uint64(m.ID), ClusterID: uint64(s.cl.ID())}
- metadata, merr := md.Marshal()
+ m := s.cl.MemberByName(s.name) //nolint:staticcheck // See https://github.com/dominikh/go-tools/issues/1698
+ md := &etcdserverpb.Metadata{NodeID: new(uint64(m.ID)), ClusterID: new(uint64(s.cl.ID()))}
+ metadata, merr := proto.Marshal(md)
if merr != nil {
return nil, merr
}
@@ -540,54 +541,54 @@ func (s *v3Manager) saveWALAndSnap() (*raftpb.HardState, error) {
peers[i] = raft.Peer{ID: uint64(id), Context: ctx}
}
- ents := make([]raftpb.Entry, len(peers))
+ ents := make([]*raftpb.Entry, len(peers))
nodeIDs := make([]uint64, len(peers))
for i, p := range peers {
nodeIDs[i] = p.ID
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: p.ID,
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(p.ID),
Context: p.Context,
}
- d, err := cc.Marshal()
+ d, err := proto.Marshal(&cc)
if err != nil {
return nil, err
}
- ents[i] = raftpb.Entry{
- Type: raftpb.EntryConfChange,
- Term: 1,
- Index: uint64(i + 1),
+ ents[i] = &raftpb.Entry{
+ Type: raftpb.EntryConfChange.Enum(),
+ Term: new(uint64(1)),
+ Index: new(uint64(i + 1)),
Data: d,
}
}
commit, term := uint64(len(ents)), uint64(1)
hardState := raftpb.HardState{
- Term: term,
- Vote: peers[0].ID,
- Commit: commit,
+ Term: new(term),
+ Vote: new(peers[0].ID),
+ Commit: new(commit),
}
- if err := w.Save(hardState, ents); err != nil {
+ if err := w.Save(&hardState, ents); err != nil {
return nil, err
}
- confState := raftpb.ConfState{
+ confState := &raftpb.ConfState{
Voters: nodeIDs,
}
raftSnap := raftpb.Snapshot{
Data: etcdserver.GetMembershipInfoInV2Format(s.lg, s.cl),
- Metadata: raftpb.SnapshotMetadata{
- Index: commit,
- Term: term,
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(commit),
+ Term: new(term),
ConfState: confState,
},
}
sn := snap.New(s.lg, s.snapDir)
- if err := sn.SaveSnap(raftSnap); err != nil {
+ if err := sn.SaveSnap(&raftSnap); err != nil {
return nil, err
}
- snapshot := walpb.Snapshot{Index: commit, Term: term, ConfState: &confState}
- return &hardState, w.SaveSnapshot(snapshot)
+ snapshot := walpb.Snapshot{Index: new(commit), Term: new(term), ConfState: confState}
+ return &hardState, w.SaveSnapshot(&snapshot)
}
func (s *v3Manager) updateCIndex(commit uint64, term uint64) error {
diff --git a/etcdutl/snapshot/v3_snapshot_test.go b/etcdutl/snapshot/v3_snapshot_test.go
index 1971eefe2fc2..0fcc3540bd70 100644
--- a/etcdutl/snapshot/v3_snapshot_test.go
+++ b/etcdutl/snapshot/v3_snapshot_test.go
@@ -15,7 +15,6 @@
package snapshot
import (
- "context"
"errors"
"path/filepath"
"strconv"
@@ -44,7 +43,7 @@ func TestSnapshotStatus(t *testing.T) {
status, err := NewV3(zap.NewNop()).Status(dbpath)
require.NoError(t, err)
- assert.Equal(t, uint32(0x62132b4d), status.Hash)
+ assert.Equal(t, uint32(0xe7a6e44b), status.Hash)
assert.Equal(t, int64(11), status.Revision)
}
@@ -136,7 +135,7 @@ func TestSnapshotStatusTotalKey(t *testing.T) {
Key: []byte(key),
Value: val,
}
- _, err := srv.Put(context.TODO(), &req)
+ _, err := srv.Put(t.Context(), &req)
require.NoError(t, err)
}
}
@@ -150,10 +149,10 @@ func TestSnapshotStatusTotalKey(t *testing.T) {
key := []byte("key1")
for i := 0; i < 3; i++ {
if i < 2 {
- _, err := srv.Put(context.TODO(), &etcdserverpb.PutRequest{Key: key, Value: []byte(strconv.Itoa(i))})
+ _, err := srv.Put(t.Context(), &etcdserverpb.PutRequest{Key: key, Value: []byte(strconv.Itoa(i))})
require.NoError(t, err)
} else {
- _, err := srv.DeleteRange(context.TODO(), &etcdserverpb.DeleteRangeRequest{Key: key})
+ _, err := srv.DeleteRange(t.Context(), &etcdserverpb.DeleteRangeRequest{Key: key})
require.NoError(t, err)
}
}
@@ -166,9 +165,9 @@ func TestSnapshotStatusTotalKey(t *testing.T) {
// key1: create -> delete -> re-create -> delete
key := []byte("key1")
for i := 0; i < 2; i++ {
- _, err := srv.Put(context.TODO(), &etcdserverpb.PutRequest{Key: key, Value: make([]byte, 1)})
+ _, err := srv.Put(t.Context(), &etcdserverpb.PutRequest{Key: key, Value: make([]byte, 1)})
require.NoError(t, err)
- _, err = srv.DeleteRange(context.TODO(), &etcdserverpb.DeleteRangeRequest{Key: key})
+ _, err = srv.DeleteRange(t.Context(), &etcdserverpb.DeleteRangeRequest{Key: key})
require.NoError(t, err)
}
},
@@ -181,10 +180,10 @@ func TestSnapshotStatusTotalKey(t *testing.T) {
key := []byte("key1")
for i := 0; i < 5; i++ {
if i%2 == 0 {
- _, err := srv.Put(context.TODO(), &etcdserverpb.PutRequest{Key: key, Value: make([]byte, 1)})
+ _, err := srv.Put(t.Context(), &etcdserverpb.PutRequest{Key: key, Value: make([]byte, 1)})
require.NoError(t, err)
} else {
- _, err := srv.DeleteRange(context.TODO(), &etcdserverpb.DeleteRangeRequest{Key: key})
+ _, err := srv.DeleteRange(t.Context(), &etcdserverpb.DeleteRangeRequest{Key: key})
require.NoError(t, err)
}
}
@@ -195,11 +194,11 @@ func TestSnapshotStatusTotalKey(t *testing.T) {
name: "mixed deletions",
prepare: func(srv *etcdserver.EtcdServer) {
// Put("key1") -> Put("key2")-> Delete("key1")
- _, err := srv.Put(context.TODO(), &etcdserverpb.PutRequest{Key: []byte("key1"), Value: make([]byte, 1)})
+ _, err := srv.Put(t.Context(), &etcdserverpb.PutRequest{Key: []byte("key1"), Value: make([]byte, 1)})
require.NoError(t, err)
- _, err = srv.Put(context.TODO(), &etcdserverpb.PutRequest{Key: []byte("key2"), Value: make([]byte, 1)})
+ _, err = srv.Put(t.Context(), &etcdserverpb.PutRequest{Key: []byte("key2"), Value: make([]byte, 1)})
require.NoError(t, err)
- _, err = srv.DeleteRange(context.TODO(), &etcdserverpb.DeleteRangeRequest{Key: []byte("key1")})
+ _, err = srv.DeleteRange(t.Context(), &etcdserverpb.DeleteRangeRequest{Key: []byte("key1")})
require.NoError(t, err)
},
expected: 1,
@@ -228,7 +227,7 @@ func insertKeys(t *testing.T, numKeys, valueSize int) func(*etcdserver.EtcdServe
Key: []byte(strconv.Itoa(i)),
Value: val,
}
- _, err := srv.Put(context.TODO(), &req)
+ _, err := srv.Put(t.Context(), &req)
require.NoError(t, err)
}
}
diff --git a/go.mod b/go.mod
index ea4b44413245..e23dd6928a5e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,12 @@
module go.etcd.io/etcd/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
replace (
go.etcd.io/etcd/api/v3 => ./api
+ go.etcd.io/etcd/cache/v3 => ./cache
go.etcd.io/etcd/client/pkg/v3 => ./client/pkg
go.etcd.io/etcd/client/v3 => ./client/v3
go.etcd.io/etcd/etcdctl/v3 => ./etcdctl
@@ -17,87 +18,90 @@ replace (
require (
github.com/bgentry/speakeasy v0.2.0
- github.com/cheggaaa/pb/v3 v3.1.6
+ github.com/cheggaaa/pb/v3 v3.1.7
github.com/coreos/go-semver v0.3.1
github.com/dustin/go-humanize v1.0.1
+ github.com/golang/protobuf v1.5.4
+ github.com/google/go-cmp v0.7.0
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
- go.etcd.io/bbolt v1.4.3
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.etcd.io/etcd/client/v3 v3.6.10
- go.etcd.io/etcd/etcdctl/v3 v3.6.10
- go.etcd.io/etcd/etcdutl/v3 v3.6.10
- go.etcd.io/etcd/pkg/v3 v3.6.10
- go.etcd.io/etcd/server/v3 v3.6.10
- go.etcd.io/etcd/tests/v3 v3.6.10
- go.etcd.io/raft/v3 v3.6.0
- go.uber.org/zap v1.27.0
- golang.org/x/time v0.9.0
- google.golang.org/grpc v1.79.3
- google.golang.org/protobuf v1.36.10
+ go.etcd.io/bbolt v1.5.0
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ go.etcd.io/etcd/etcdctl/v3 v3.7.0
+ go.etcd.io/etcd/etcdutl/v3 v3.7.0
+ go.etcd.io/etcd/pkg/v3 v3.7.0
+ go.etcd.io/etcd/server/v3 v3.7.0
+ go.etcd.io/etcd/tests/v3 v3.7.0
+ go.etcd.io/raft/v3 v3.7.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/time v0.15.0
+ golang.org/x/tools v0.45.0
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
)
require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/fatih/color v1.18.0 // indirect
+ github.com/clipperhouse/displaywidth v0.11.0 // indirect
+ github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/fatih/color v1.19.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
- github.com/golang/protobuf v1.5.4 // indirect
- github.com/google/btree v1.1.3 // indirect
- github.com/google/go-cmp v0.7.0 // indirect
+ github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/olekukonko/tablewriter v0.0.5 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_golang v1.20.5 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.62.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
+ github.com/olekukonko/errors v1.2.0 // indirect
+ github.com/olekukonko/ll v0.1.6 // indirect
+ github.com/olekukonko/tablewriter v1.1.4 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/client_golang v1.23.2 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/sirupsen/logrus v1.9.4 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
- github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
+ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect
go.etcd.io/gofail v0.2.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
- go.opentelemetry.io/otel v1.40.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
- go.opentelemetry.io/otel/metric v1.40.0 // indirect
- go.opentelemetry.io/otel/sdk v1.40.0 // indirect
- go.opentelemetry.io/otel/trace v1.40.0 // indirect
- go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
+ go.opentelemetry.io/otel v1.43.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
+ go.opentelemetry.io/otel/metric v1.43.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.43.0 // indirect
+ go.opentelemetry.io/otel/trace v1.43.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/crypto v0.48.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/crypto v0.52.0 // indirect
+ golang.org/x/mod v0.36.0 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sync v0.20.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
- sigs.k8s.io/yaml v1.4.0 // indirect
+ k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/go.sum b/go.sum
index 4470dc6e552a..aafa57c06f46 100644
--- a/go.sum
+++ b/go.sum
@@ -1,88 +1,62 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
-github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
+github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
+github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
+github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -93,35 +67,35 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
+github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
+github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
+github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
+github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
+github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
+github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
+github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
@@ -130,143 +104,105 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
+go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA=
go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o=
-go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
-go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
+go.etcd.io/raft/v3 v3.7.0 h1:BGzlwx07bLv8PW6OU5HObuz1y4hlPZUXA07pM1mPUh4=
+go.etcd.io/raft/v3 v3.7.0/go.mod h1:6gX6T2X907DjnjsFLODnTxba77stjs84W9gTTI0GUNA=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
-go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
-go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
-go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
-go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
-go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
-go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
-go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
-go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
-go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
-go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
-go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
+go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
+golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
+golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
+golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
+gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/go.work b/go.work
new file mode 100644
index 000000000000..a45b75f624a2
--- /dev/null
+++ b/go.work
@@ -0,0 +1,21 @@
+// This is a generated file. Do not edit directly.
+
+go 1.26
+
+toolchain go1.26.5
+
+use (
+ .
+ ./api
+ ./cache
+ ./client/pkg
+ ./client/v3
+ ./etcdctl
+ ./etcdutl
+ ./pkg
+ ./server
+ ./tests
+ ./tools/mod
+ ./tools/rw-heatmaps
+ ./tools/testgrid-analysis
+)
diff --git a/go.work.sum b/go.work.sum
new file mode 100644
index 000000000000..cfcbbec3255a
--- /dev/null
+++ b/go.work.sum
@@ -0,0 +1,551 @@
+bitbucket.org/creachadair/shell v0.0.8 h1:3yM6JcAfaGWzjzcCamTblzSIWXm/YSs0PFGIzBm2HTo=
+bitbucket.org/creachadair/shell v0.0.8/go.mod h1:vINzudofoUXZSJ5tREgpy+Etyjsag3ait5WOWImEVZ0=
+bitbucket.org/creachadair/stringset v0.0.11 h1:6Sv4CCv14Wm+OipW4f3tWOb0SQVpBDLW0knnJqUnmZ8=
+bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c h1:bkb2NMGo3/Du52wvYj9Whth5KZfMV6d3O0Vbr3nz/UE=
+bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 h1:YhMSc48s25kr7kv31Z8vf7sPUIq5YJva9z1mn/hAt0M=
+buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
+buf.build/go/protovalidate v0.12.0 h1:4GKJotbspQjRCcqZMGVSuC8SjwZ/FmgtSuKDpKUTZew=
+buf.build/go/protovalidate v0.12.0/go.mod h1:q3PFfbzI05LeqxSwq+begW2syjy2Z6hLxZSkP1OH/D0=
+cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
+cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
+cloud.google.com/go v0.121.2 h1:v2qQpN6Dx9x2NmwrqlesOt3Ys4ol5/lFZ6Mg1B7OJCg=
+cloud.google.com/go v0.121.2/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
+cloud.google.com/go/accessapproval v1.7.1 h1:/5YjNhR6lzCvmJZAnByYkfEgWjfAKwYP6nkuTk6nKFE=
+cloud.google.com/go/accesscontextmanager v1.8.1 h1:WIAt9lW9AXtqw/bnvrEUaE8VG/7bAAeMzRCBGMkc4+w=
+cloud.google.com/go/aiplatform v1.48.0 h1:M5davZWCTzE043rJCn+ZLW6hSxfG1KAx4vJTtas2/ec=
+cloud.google.com/go/analytics v0.21.3 h1:TFBC1ZAqX9/jL56GEXdLrVe5vT3I22bDVWyDwZX4IEg=
+cloud.google.com/go/apigateway v1.6.1 h1:aBSwCQPcp9rZ0zVEUeJbR623palnqtvxJlUyvzsKGQc=
+cloud.google.com/go/apigeeconnect v1.6.1 h1:6u/jj0P2c3Mcm+H9qLsXI7gYcTiG9ueyQL3n6vCmFJM=
+cloud.google.com/go/apigeeregistry v0.7.1 h1:hgq0ANLDx7t2FDZDJQrCMtCtddR/pjCqVuvQWGrQbXw=
+cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ=
+cloud.google.com/go/appengine v1.8.1 h1:J+aaUZ6IbTpBegXbmEsh8qZZy864ZVnOoWyfa1XSNbI=
+cloud.google.com/go/area120 v0.8.1 h1:wiOq3KDpdqXmaHzvZwKdpoM+3lDcqsI2Lwhyac7stss=
+cloud.google.com/go/artifactregistry v1.14.1 h1:k6hNqab2CubhWlGcSzunJ7kfxC7UzpAfQ1UPb9PDCKI=
+cloud.google.com/go/asset v1.14.1 h1:vlHdznX70eYW4V1y1PxocvF6tEwxJTTarwIGwOhFF3U=
+cloud.google.com/go/assuredworkloads v1.11.1 h1:yaO0kwS+SnhVSTF7BqTyVGt3DTocI6Jqo+S3hHmCwNk=
+cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
+cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
+cloud.google.com/go/automl v1.13.1 h1:iP9iQurb0qbz+YOOMfKSEjhONA/WcoOIjt6/m+6pIgo=
+cloud.google.com/go/baremetalsolution v1.1.1 h1:0Ge9PQAy6cZ1tRrkc44UVgYV15nw2TVnzJzYsMHXF+E=
+cloud.google.com/go/batch v1.3.1 h1:uE0Q//W7FOGPjf7nuPiP0zoE8wOT3ngoIO2HIet0ilY=
+cloud.google.com/go/beyondcorp v1.0.0 h1:VPg+fZXULQjs8LiMeWdLaB5oe8G9sEoZ0I0j6IMiG1Q=
+cloud.google.com/go/bigquery v1.53.0 h1:K3wLbjbnSlxhuG5q4pntHv5AEbQM1QqHKGYgwFIqOTg=
+cloud.google.com/go/billing v1.16.0 h1:1iktEAIZ2uA6KpebC235zi/rCXDdDYQ0bTXTNetSL80=
+cloud.google.com/go/binaryauthorization v1.6.1 h1:cAkOhf1ic92zEN4U1zRoSupTmwmxHfklcp1X7CCBKvE=
+cloud.google.com/go/certificatemanager v1.7.1 h1:uKsohpE0hiobx1Eak9jNcPCznwfB6gvyQCcS28Ah9E8=
+cloud.google.com/go/channel v1.16.0 h1:dqRkK2k7Ll/HHeYGxv18RrfhozNxuTJRkspW0iaFZoY=
+cloud.google.com/go/cloudbuild v1.13.0 h1:YBbAWcvE4x6xPWTyS+OU4eiUpz5rCS3VCM/aqmfddPA=
+cloud.google.com/go/clouddms v1.6.1 h1:rjR1nV6oVf2aNNB7B5uz1PDIlBjlOiBgR+q5n7bbB7M=
+cloud.google.com/go/cloudtasks v1.12.1 h1:cMh9Q6dkvh+Ry5LAPbD/U2aw6KAqdiU6FttwhbTo69w=
+cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
+cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
+cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
+cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
+cloud.google.com/go/contactcenterinsights v1.10.0 h1:YR2aPedGVQPpFBZXJnPkqRj8M//8veIZZH5ZvICoXnI=
+cloud.google.com/go/container v1.24.0 h1:N51t/cgQJFqDD/W7Mb+IvmAPHrf8AbPx7Bb7aF4lROE=
+cloud.google.com/go/containeranalysis v0.10.1 h1:SM/ibWHWp4TYyJMwrILtcBtYKObyupwOVeceI9pNblw=
+cloud.google.com/go/datacatalog v1.16.0 h1:qVeQcw1Cz93/cGu2E7TYUPh8Lz5dn5Ws2siIuQ17Vng=
+cloud.google.com/go/dataflow v0.9.1 h1:VzG2tqsk/HbmOtq/XSfdF4cBvUWRK+S+oL9k4eWkENQ=
+cloud.google.com/go/dataform v0.8.1 h1:xcWso0hKOoxeW72AjBSIp/UfkvpqHNzzS0/oygHlcqY=
+cloud.google.com/go/datafusion v1.7.1 h1:eX9CZoyhKQW6g1Xj7+RONeDj1mV8KQDKEB9KLELX9/8=
+cloud.google.com/go/datalabeling v0.8.1 h1:zxsCD/BLKXhNuRssen8lVXChUj8VxF3ofN06JfdWOXw=
+cloud.google.com/go/dataplex v1.9.0 h1:yoBWuuUZklYp7nx26evIhzq8+i/nvKYuZr1jka9EqLs=
+cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU=
+cloud.google.com/go/dataproc/v2 v2.0.1 h1:4OpSiPMMGV3XmtPqskBU/RwYpj3yMFjtMLj/exi425Q=
+cloud.google.com/go/dataqna v0.8.1 h1:ITpUJep04hC9V7C+gcK390HO++xesQFSUJ7S4nSnF3U=
+cloud.google.com/go/datastore v1.13.0 h1:ktbC66bOQB3HJPQe8qNI1/aiQ77PMu7hD4mzE6uxe3w=
+cloud.google.com/go/datastream v1.10.0 h1:ra/+jMv36zTAGPfi8TRne1hXme+UsKtdcK4j6bnqQiw=
+cloud.google.com/go/deploy v1.13.0 h1:A+w/xpWgz99EYzB6e31gMGAI/P5jTZ2UO7veQK5jQ8o=
+cloud.google.com/go/dialogflow v1.40.0 h1:sCJbaXt6ogSbxWQnERKAzos57f02PP6WkGbOZvXUdwc=
+cloud.google.com/go/dlp v1.10.1 h1:tF3wsJ2QulRhRLWPzWVkeDz3FkOGVoMl6cmDUHtfYxw=
+cloud.google.com/go/documentai v1.22.0 h1:dW8ex9yb3oT9s1yD2+yLcU8Zq15AquRZ+wd0U+TkxFw=
+cloud.google.com/go/domains v0.9.1 h1:rqz6KY7mEg7Zs/69U6m6LMbB7PxFDWmT3QWNXIqhHm0=
+cloud.google.com/go/edgecontainer v1.1.1 h1:zhHWnLzg6AqzE+I3gzJqiIwHfjEBhWctNQEzqb+FaRo=
+cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0=
+cloud.google.com/go/essentialcontacts v1.6.2 h1:OEJ0MLXXCW/tX1fkxzEZOsv/wRfyFsvDVNaHWBAvoV0=
+cloud.google.com/go/eventarc v1.13.0 h1:xIP3XZi0Xawx8DEfh++mE2lrIi5kQmCr/KcWhJ1q0J4=
+cloud.google.com/go/filestore v1.7.1 h1:Eiz8xZzMJc5ppBWkuaod/PUdUZGCFR8ku0uS+Ah2fRw=
+cloud.google.com/go/firestore v1.11.0 h1:PPgtwcYUOXV2jFe1bV3nda3RCrOa8cvBjTOn2MQVfW8=
+cloud.google.com/go/functions v1.15.1 h1:LtAyqvO1TFmNLcROzHZhV0agEJfBi+zfMZsF4RT/a7U=
+cloud.google.com/go/gaming v1.10.1 h1:5qZmZEWzMf8GEFgm9NeC3bjFRpt7x4S6U7oLbxaf7N8=
+cloud.google.com/go/gkebackup v1.3.0 h1:lgyrpdhtJKV7l1GM15YFt+OCyHMxsQZuSydyNmS0Pxo=
+cloud.google.com/go/gkeconnect v0.8.1 h1:a1ckRvVznnuvDWESM2zZDzSVFvggeBaVY5+BVB8tbT0=
+cloud.google.com/go/gkehub v0.14.1 h1:2BLSb8i+Co1P05IYCKATXy5yaaIw/ZqGvVSBTLdzCQo=
+cloud.google.com/go/gkemulticloud v1.0.0 h1:MluqhtPVZReoriP5+adGIw+ij/RIeRik8KApCW2WMTw=
+cloud.google.com/go/grafeas v0.3.0 h1:oyTL/KjiUeBs9eYLw/40cpSZglUC+0F7X4iu/8t7NWs=
+cloud.google.com/go/gsuiteaddons v1.6.1 h1:mi9jxZpzVjLQibTS/XfPZvl+Jr6D5Bs8pGqUjllRb00=
+cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
+cloud.google.com/go/iap v1.8.1 h1:X1tcp+EoJ/LGX6cUPt3W2D4H2Kbqq0pLAsldnsCjLlE=
+cloud.google.com/go/ids v1.4.1 h1:khXYmSoDDhWGEVxHl4c4IgbwSRR+qE/L4hzP3vaU9Hc=
+cloud.google.com/go/iot v1.7.1 h1:yrH0OSmicD5bqGBoMlWG8UltzdLkYzNUwNVUVz7OT54=
+cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs=
+cloud.google.com/go/language v1.10.1 h1:3MXeGEv8AlX+O2LyV4pO4NGpodanc26AmXwOuipEym0=
+cloud.google.com/go/lifesciences v0.9.1 h1:axkANGx1wiBXHiPcJZAE+TDjjYoJRIDzbHC/WYllCBU=
+cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I=
+cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
+cloud.google.com/go/managedidentities v1.6.1 h1:2/qZuOeLgUHorSdxSQGtnOu9xQkBn37+j+oZQv/KHJY=
+cloud.google.com/go/maps v1.4.0 h1:PdfgpBLhAoSzZrQXP+/zBc78fIPLZSJp5y8+qSMn2UU=
+cloud.google.com/go/mediatranslation v0.8.1 h1:50cF7c1l3BanfKrpnTCaTvhf+Fo6kdF21DG0byG7gYU=
+cloud.google.com/go/memcache v1.10.1 h1:7lkLsF0QF+Mre0O/NvkD9Q5utUNwtzvIYjrOLOs0HO0=
+cloud.google.com/go/metastore v1.12.0 h1:+9DsxUOHvsqvC0ylrRc/JwzbXJaaBpfIK3tX0Lx8Tcc=
+cloud.google.com/go/monitoring v1.17.0 h1:blrdvF0MkPPivSO041ihul7rFMhXdVp8Uq7F59DKXTU=
+cloud.google.com/go/monitoring v1.17.0/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw=
+cloud.google.com/go/networkconnectivity v1.12.1 h1:LnrYM6lBEeTq+9f2lR4DjBhv31EROSAQi/P5W4Q0AEc=
+cloud.google.com/go/networkmanagement v1.8.0 h1:/3xP37eMxnyvkfLrsm1nv1b2FbMMSAEAOlECTvoeCq4=
+cloud.google.com/go/networksecurity v0.9.1 h1:TBLEkMp3AE+6IV/wbIGRNTxnqLXHCTEQWoxRVC18TzY=
+cloud.google.com/go/notebooks v1.9.1 h1:CUqMNEtv4EHFnbogV+yGHQH5iAQLmijOx191innpOcs=
+cloud.google.com/go/optimization v1.4.1 h1:pEwOAmO00mxdbesCRSsfj8Sd4rKY9kBrYW7Vd3Pq7cA=
+cloud.google.com/go/orchestration v1.8.1 h1:KmN18kE/xa1n91cM5jhCh7s1/UfIguSCisw7nTMUzgE=
+cloud.google.com/go/orgpolicy v1.11.1 h1:I/7dHICQkNwym9erHqmlb50LRU588NPCvkfIY0Bx9jI=
+cloud.google.com/go/osconfig v1.12.1 h1:dgyEHdfqML6cUW6/MkihNdTVc0INQst0qSE8Ou1ub9c=
+cloud.google.com/go/oslogin v1.10.1 h1:LdSuG3xBYu2Sgr3jTUULL1XCl5QBx6xwzGqzoDUw1j0=
+cloud.google.com/go/phishingprotection v0.8.1 h1:aK/lNmSd1vtbft/vLe2g7edXK72sIQbqr2QyrZN/iME=
+cloud.google.com/go/policytroubleshooter v1.8.0 h1:XTMHy31yFmXgQg57CB3w9YQX8US7irxDX0Fl0VwlZyY=
+cloud.google.com/go/privatecatalog v0.9.1 h1:B/18xGo+E0EMS9LOEQ0zXz7F2asMgmVgTYGSI89MHOA=
+cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g=
+cloud.google.com/go/pubsublite v1.8.1 h1:pX+idpWMIH30/K7c0epN6V703xpIcMXWRjKJsz0tYGY=
+cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ=
+cloud.google.com/go/recaptchaenterprise/v2 v2.7.2 h1:IGkbudobsTXAwmkEYOzPCQPApUCsN4Gbq3ndGVhHQpI=
+cloud.google.com/go/recommendationengine v0.8.1 h1:nMr1OEVHuDambRn+/y4RmNAmnR/pXCuHtH0Y4tCgGRQ=
+cloud.google.com/go/recommender v1.10.1 h1:UKp94UH5/Lv2WXSQe9+FttqV07x/2p1hFTMMYVFtilg=
+cloud.google.com/go/redis v1.13.1 h1:YrjQnCC7ydk+k30op7DSjSHw1yAYhqYXFcOq1bSXRYA=
+cloud.google.com/go/resourcemanager v1.9.1 h1:QIAMfndPOHR6yTmMUB0ZN+HSeRmPjR/21Smq5/xwghI=
+cloud.google.com/go/resourcesettings v1.6.1 h1:Fdyq418U69LhvNPFdlEO29w+DRRjwDA4/pFamm4ksAg=
+cloud.google.com/go/retail v1.14.1 h1:gYBrb9u/Hc5s5lUTFXX1Vsbc/9BEvgtioY6ZKaK0DK8=
+cloud.google.com/go/run v1.2.0 h1:kHeIG8q+N6Zv0nDkBjSOYfK2eWqa5FnaiDPH/7/HirE=
+cloud.google.com/go/scheduler v1.10.1 h1:yoZbZR8880KgPGLmACOMCiY2tPk+iX4V/dkxqTirlz8=
+cloud.google.com/go/secretmanager v1.11.1 h1:cLTCwAjFh9fKvU6F13Y4L9vPcx9yiWPyWXE4+zkuEQs=
+cloud.google.com/go/security v1.15.1 h1:jR3itwycg/TgGA0uIgTItcVhA55hKWiNJxaNNpQJaZE=
+cloud.google.com/go/securitycenter v1.23.0 h1:XOGJ9OpnDtqg8izd7gYk/XUhj8ytjIalyjjsR6oyG0M=
+cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE=
+cloud.google.com/go/servicedirectory v1.11.0 h1:pBWpjCFVGWkzVTkqN3TBBIqNSoSHY86/6RL0soSQ4z8=
+cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY=
+cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4=
+cloud.google.com/go/shell v1.7.1 h1:aHbwH9LSqs4r2rbay9f6fKEls61TAjT63jSyglsw7sI=
+cloud.google.com/go/spanner v1.47.0 h1:aqiMP8dhsEXgn9K5EZBWxPG7dxIiyM2VaikqeU4iteg=
+cloud.google.com/go/speech v1.19.0 h1:MCagaq8ObV2tr1kZJcJYgXYbIn8Ai5rp42tyGYw9rls=
+cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
+cloud.google.com/go/storagetransfer v1.10.0 h1:+ZLkeXx0K0Pk5XdDmG0MnUVqIR18lllsihU/yq39I8Q=
+cloud.google.com/go/talent v1.6.2 h1:j46ZgD6N2YdpFPux9mc7OAf4YK3tiBCsbLKc8rQx+bU=
+cloud.google.com/go/texttospeech v1.7.1 h1:S/pR/GZT9p15R7Y2dk2OXD/3AufTct/NSxT4a7nxByw=
+cloud.google.com/go/tpu v1.6.1 h1:kQf1jgPY04UJBYYjNUO+3GrZtIb57MfGAW2bwgLbR3A=
+cloud.google.com/go/trace v1.10.4 h1:2qOAuAzNezwW3QN+t41BtkDJOG42HywL73q8x/f6fnM=
+cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY=
+cloud.google.com/go/translate v1.8.2 h1:PQHamiOzlehqLBJMnM72lXk/OsMQewZB12BKJ8zXrU0=
+cloud.google.com/go/video v1.19.0 h1:BRyyS+wU+Do6VOXnb8WfPr42ZXti9hzmLKLUCkggeK4=
+cloud.google.com/go/videointelligence v1.11.1 h1:MBMWnkQ78GQnRz5lfdTAbBq/8QMCF3wahgtHh3s/J+k=
+cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4=
+cloud.google.com/go/vision/v2 v2.7.2 h1:ccK6/YgPfGHR/CyESz1mvIbsht5Y2xRsWCPqmTNydEw=
+cloud.google.com/go/vmmigration v1.7.1 h1:gnjIclgqbEMc+cF5IJuPxp53wjBIlqZ8h9hE8Rkwp7A=
+cloud.google.com/go/vmwareengine v1.0.0 h1:qsJ0CPlOQu/3MFBGklu752v3AkD+Pdu091UmXJ+EjTA=
+cloud.google.com/go/vpcaccess v1.7.1 h1:ram0GzjNWElmbxXMIzeOZUkQ9J8ZAahD6V8ilPGqX0Y=
+cloud.google.com/go/webrisk v1.9.1 h1:Ssy3MkOMOnyRV5H2bkMQ13Umv7CwB/kugo3qkAX83Fk=
+cloud.google.com/go/websecurityscanner v1.6.1 h1:CfEF/vZ+xXyAR3zC9iaC/QRdf1MEgS20r5UR17Q4gOg=
+cloud.google.com/go/workflows v1.11.1 h1:2akeQ/PgtRhrNuD/n1WvJd5zb7YyuDZrlOanBj2ihPg=
+codeberg.org/go-fonts/stix v0.3.0 h1:vHI1LmLWEcAdcf+5aRMtA1eYKJJ9ZjetVstBD/dRe1Q=
+codeberg.org/go-fonts/stix v0.3.0/go.mod h1:1OSJSnA/PoHqbW2tjkkqTmNPp5xTtJQN2GRXJjO/+WA=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4=
+contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
+gioui.org v0.2.0 h1:RbzDn1h/pCVf/q44ImQSa/J3MIFpY3OWphzT/Tyei+w=
+gioui.org v0.2.0/go.mod h1:1H72sKEk/fNFV+l0JNeM2Dt3co3Y4uaQcD+I+/GQ0e4=
+gioui.org/cpu v0.0.0-20220412190645-f1e9e8c3b1f7 h1:tNJdnP5CgM39PRc+KWmBRRYX/zJ+rd5XaYxY5d5veqA=
+gioui.org/cpu v0.0.0-20220412190645-f1e9e8c3b1f7/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
+gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y=
+gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
+gioui.org/x v0.2.0 h1:/MbdjKH19F16auv19UiQxli2n6BYPw7eyh9XBOTgmEw=
+gioui.org/x v0.2.0/go.mod h1:rCGN2nZ8ZHqrtseJoQxCMZpt2xrZUrdZ2WuMRLBJmYs=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
+github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
+github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 h1:xlwdaKcTNVW4PtpQb8aKA4Pjy0CdJHEqvFbAnvR5m2g=
+github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
+github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM=
+github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8=
+github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
+github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
+github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
+github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
+github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0 h1:uF5Q/hWnDU1XZeT6CsrRSxHLroUSEYYO3kgES+yd+So=
+github.com/andybalholm/stroke v0.0.0-20221221101821-bd29b49d73f0/go.mod h1:ccdDYaY5+gO+cbnQdFxEXqfy0RkoV25H3jLXUDNM3wg=
+github.com/anthropics/anthropic-sdk-go v1.38.0 h1:bA4DcK+91gorIX+5VTONnynyt9LRU4nnN6rRQ+j/NIg=
+github.com/anthropics/anthropic-sdk-go v1.38.0/go.mod h1:d288C1L+m74OYuYBvc4UFtR1Q8J0gC55oYDh2t+XxdI=
+github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI=
+github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM=
+github.com/apache/arrow/go/v12 v12.0.0 h1:xtZE63VWl7qLdB0JObIXvvhGjoVNrQ9ciIHG2OK5cmc=
+github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
+github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M=
+github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
+github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
+github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
+github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
+github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
+github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
+github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
+github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
+github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
+github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
+github.com/bwesterb/go-ristretto v1.2.0 h1:xxWOVbN5m8NNKiSDZXE1jtZvZnC6JSJ9cYFADiZcWtw=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
+github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
+github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
+github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
+github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
+github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
+github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
+github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
+github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
+github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
+github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
+github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
+github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a h1:8d1CEOF1xldesKds5tRG3tExBsMOgWYownMHNCsev54=
+github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
+github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
+github.com/cloudflare/redoctober v0.0.0-20211013234631-6a74ccc611f6 h1:QKzett0dn5FhjcIHNKSClEilabfhWCnsdijq3ftm9Ms=
+github.com/cloudflare/redoctober v0.0.0-20211013234631-6a74ccc611f6/go.mod h1:Ikt4Wfpln1YOrak+auA8BNxgiilj0Y2y7nO+aN2eMzk=
+github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
+github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
+github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
+github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
+github.com/cristalhq/acmd v0.12.0 h1:RdlKnxjN+txbQosg8p/TRNZ+J1Rdne43MVQZ1zDhGWk=
+github.com/cristalhq/acmd v0.12.0/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
+github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
+github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
+github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
+github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
+github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
+github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
+github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
+github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
+github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
+github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
+github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
+github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
+github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
+github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
+github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
+github.com/fullstorydev/grpcurl v1.8.9 h1:JMvZXK8lHDGyLmTQ0ZdGDnVVGuwjbpaumf8p42z0d+c=
+github.com/fullstorydev/grpcurl v1.8.9/go.mod h1:PNNKevV5VNAV2loscyLISrEnWQI61eqR0F8l3bVadAA=
+github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
+github.com/getsentry/sentry-go v0.11.0 h1:qro8uttJGvNAMr5CLcFI9CHR0aDzXl0Vs3Pmw/oTPg8=
+github.com/getsentry/sentry-go v0.11.0/go.mod h1:KBQIxiZAetw62Cj8Ri964vAEWVdgfaUCn30Q3bCvANo=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
+github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ=
+github.com/go-fonts/latin-modern v0.2.0 h1:5/Tv1Ek/QCr20C6ZOz15vw3g7GELYL98KWr8Hgo+3vk=
+github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM=
+github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
+github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
+github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
+github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
+github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ=
+github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ=
+github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
+github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
+github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
+github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
+github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
+github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
+github.com/goccmack/gocc v1.0.2 h1:PHv20lcM1Erz+kovS+c07DnDFp6X5cvghndtTXuEyfE=
+github.com/goccmack/gocc v1.0.2/go.mod h1:LXX2tFVUggS/Zgx/ICPOr3MLyusuM7EcbfkPvNsjdO8=
+github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
+github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
+github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
+github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
+github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM=
+github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-github/v50 v50.2.0 h1:j2FyongEHlO9nxXLc+LP3wuBSVU9mVxfpdYUexMpIfk=
+github.com/google/go-pkcs11 v0.2.0 h1:5meDPB26aJ98f+K9G21f0AqZwo/S5BJMJh8nuhMbdsI=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
+github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
+github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
+github.com/google/trillian v1.6.0 h1:jMBeDBIkINFvS2n6oV5maDqfRlxREAc6CW9QYWQ0qT4=
+github.com/google/trillian v1.6.0/go.mod h1:Yu3nIMITzNhhMJEHjAtp6xKiu+H/iHu2Oq5FjV2mCWI=
+github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
+github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
+github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
+github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
+github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
+github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4=
+github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
+github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
+github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
+github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
+github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
+github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
+github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
+github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls=
+github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
+github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
+github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
+github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
+github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
+github.com/kylelemons/go-gypsy v1.0.0 h1:7/wQ7A3UL1bnqRMnZ6T8cwCOArfZCxFmb1iTxaOOo1s=
+github.com/kylelemons/go-gypsy v1.0.0/go.mod h1:chkXM0zjdpXOiqkCW1XcCHDfjfk14PH2KKkQWxfJUcU=
+github.com/letsencrypt/pkcs11key/v4 v4.0.0 h1:qLc/OznH7xMr5ARJgkZCCWk+EomQkiNTOoOF5LAgagc=
+github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M=
+github.com/lyft/protoc-gen-star/v2 v2.0.1 h1:keaAo8hRuAT0O3DfJ/wM3rufbAjGeJ1lAtWZHDjKGB0=
+github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
+github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
+github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/mgechev/dots v1.0.0 h1:o+4OJ3OjWzgQHGJXKfJ8rbH4dqDugu5BiEy84nxg0k4=
+github.com/mgechev/dots v1.0.0/go.mod h1:rykuMydC9t3wfkM+ccYH3U3ss03vZGg6h3hmOznXLH0=
+github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
+github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=
+github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=
+github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/mozilla/tls-observatory v0.0.0-20250923143331-eef96233227e h1:gOlpekCwR+xjqedQsHo1c7aUSixaQUIe3sAcEeDCMLc=
+github.com/mozilla/tls-observatory v0.0.0-20250923143331-eef96233227e/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
+github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d h1:tLWCMSjfL8XyZwpu1RzI2UpJSPbZCOZ6DVHQFnlpL7A=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
+github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
+github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
+github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
+github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
+github.com/openai/openai-go/v3 v3.32.0 h1:aHp/3wkX1W6jB8zTtf9xV0aK0qPFSVDqS7AHmlJ4hXs=
+github.com/openai/openai-go/v3 v3.32.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo=
+github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
+github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc=
+github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ=
+github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ=
+github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs=
+github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
+github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
+github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
+github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/prometheus/prometheus v0.47.2 h1:jWcnuQHz1o1Wu3MZ6nMJDuTI0kU5yJp9pkxh8XEkNvI=
+github.com/prometheus/prometheus v0.47.2/go.mod h1:J/bmOSjgH7lFxz2gZhrWEZs2i64vMS+HIuZfmYNhJ/M=
+github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71 h1:CNooiryw5aisadVfzneSZPswRWvnVW8hF1bS/vo8ReI=
+github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
+github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
+github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY=
+github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec=
+github.com/shirou/gopsutil/v4 v4.26.4 h1:B4SXVbcwTyrocPHEmWBC4uCYr4Xcu3MK1TXqbprAOWY=
+github.com/shirou/gopsutil/v4 v4.26.4/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
+github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
+github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
+github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
+github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
+github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
+github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
+github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
+github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
+github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
+github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdqCysW5k=
+github.com/valyala/quicktemplate v1.8.0/go.mod h1:qIqW8/igXt8fdrUln5kOSb+KWMaJ4Y8QUsfd1k6L2jM=
+github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
+github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
+github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
+github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
+github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
+github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
+github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
+github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
+github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30=
+github.com/zmap/zcertificate v0.0.1 h1:2X15TRx4Fr6qzKItfwUdww294OeRSmHILLa+Xn2Uv+s=
+go.etcd.io/bbolt v1.5.0-rc.0/go.mod h1:HXpeuv7FrPaEH3z9FzbnSXSntD27WQyEMjQNJbd+vd8=
+go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI=
+go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
+go.etcd.io/etcd/raft/v3 v3.5.12 h1:7r22RufdDsq2z3STjoR7Msz6fYH8tmbkdheGfwJNRmU=
+go.etcd.io/etcd/raft/v3 v3.5.12/go.mod h1:ERQuZVe79PI6vcC3DlKBukDCLja/L7YMu29B74Iwj4U=
+go.etcd.io/raft/v3 v3.7.0-rc.1/go.mod h1:sWN3FZ3+MUeGidw1pzYc78c11I7VmeteCFB3YjA7tcw=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
+go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
+golang.org/x/exp/shiny v0.0.0-20241009180824-f66d83c29e7c h1:jTMrjjZRcSH/BDxWhXCP6OWsfVgmnwI7J+F4/nyVXaU=
+golang.org/x/exp/shiny v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
+golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
+golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
+golang.org/x/perf v0.0.0-20250813145418-2f7363a06fe1 h1:stGRioFgvBd3x8HoGVg9bb41lLTWLjBMFT/dMB7f4mQ=
+golang.org/x/perf v0.0.0-20250813145418-2f7363a06fe1/go.mod h1:rjfRjhHXb3XNVh/9i5Jr2tXoTd0vOlZN5rzsM8cQE6k=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
+golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
+gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c h1:cJWOvXtcaFSGXz2F4z2AMM0VV7edDDGrxb5GLQH7ayQ=
+gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c/go.mod h1:fy6Otjqbk477ELp8IXTpw1cObQtLbRCBVonY+bTTfcM=
+google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA=
+google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/genai v1.54.0 h1:ZQCa70WMTJDI11FdqWCzGvZ5PanpcpfoO6jl/lrSnGU=
+google.golang.org/genai v1.54.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
+google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
+google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
+google.golang.org/genproto/googleapis/bytestream v0.0.0-20230720185612-659f7aaaa771 h1:gm8vsVR64Jx1GxHY8M+p8YA2bxU/H/lymcutB2l7l9s=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
+gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
+gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs=
+k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
+k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA=
+k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg=
+lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
+modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
+modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
+modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
+modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
+modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0=
+modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
+modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
+modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
+modernc.org/sqlite v1.18.2 h1:S2uFiaNPd/vTAP/4EmyY8Qe2Quzu26A2L1e25xRNTio=
+modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
+modernc.org/tcl v1.13.2 h1:5PQgL/29XkQ9wsEmmNPjzKs+7iPCaYqUJAhzPvQbjDA=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
+rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
+rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
+rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
+sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
+sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
diff --git a/hack/benchmark/README.md b/hack/benchmark/README.md
deleted file mode 100644
index 6a09c530299d..000000000000
--- a/hack/benchmark/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Usage
-
-Benchmark 3-member etcd cluster to get its read and write performance.
-
-## Instructions
-
-1. Start 3-member etcd cluster on 3 machines
-2. Update `$leader` and `$servers` in the script
-3. Run the script in a separate machine
-
-## Caveat
-
-1. Set environment variable `GOMAXPROCS` as the number of available cores to maximize CPU resources for both etcd member and bench process.
-2. Set the number of open files per process as 10000 for amounts of client connections for both etcd member and benchmark process.
diff --git a/hack/benchmark/bench.sh b/hack/benchmark/bench.sh
deleted file mode 100755
index d72efd3e2029..000000000000
--- a/hack/benchmark/bench.sh
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/bash -e
-
-leader=http://localhost:2379
-# assume three servers
-servers=( http://localhost:2379 http://localhost:22379 http://localhost:32379 )
-
-keyarray=( 64 256 )
-
-for keysize in ${keyarray[@]}; do
-
- echo write, 1 client, $keysize key size, to leader
- ./hey -m PUT -n 10 -d value=`head -c $keysize < /dev/zero | tr '\0' '\141'` -c 1 -T application/x-www-form-urlencoded $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo write, 64 client, $keysize key size, to leader
- ./hey -m PUT -n 640 -d value=`head -c $keysize < /dev/zero | tr '\0' '\141'` -c 64 -T application/x-www-form-urlencoded $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo write, 256 client, $keysize key size, to leader
- ./hey -m PUT -n 2560 -d value=`head -c $keysize < /dev/zero | tr '\0' '\141'` -c 256 -T application/x-www-form-urlencoded $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo write, 64 client, $keysize key size, to all servers
- for i in ${servers[@]}; do
- ./hey -m PUT -n 210 -d value=`head -c $keysize < /dev/zero | tr '\0' '\141'` -c 21 -T application/x-www-form-urlencoded $i/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo &
- done
- # wait for all heys to start running
- sleep 3
- # wait for all heys to finish
- for pid in $(pgrep 'hey'); do
- while kill -0 "$pid" 2> /dev/null; do
- sleep 3
- done
- done
-
- echo write, 256 client, $keysize key size, to all servers
- for i in ${servers[@]}; do
- ./hey -m PUT -n 850 -d value=`head -c $keysize < /dev/zero | tr '\0' '\141'` -c 85 -T application/x-www-form-urlencoded $i/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo &
- done
- sleep 3
- for pid in $(pgrep 'hey'); do
- while kill -0 "$pid" 2> /dev/null; do
- sleep 3
- done
- done
-
- echo read, 1 client, $keysize key size, to leader
- ./hey -n 100 -c 1 $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo read, 64 client, $keysize key size, to leader
- ./hey -n 6400 -c 64 $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo read, 256 client, $keysize key size, to leader
- ./hey -n 25600 -c 256 $leader/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
-
- echo read, 64 client, $keysize key size, to all servers
- # bench servers one by one, so it doesn't overload this benchmark machine
- # It doesn't impact correctness because read request doesn't involve peer interaction.
- for i in ${servers[@]}; do
- ./hey -n 21000 -c 21 $i/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
- done
-
- echo read, 256 client, $keysize key size, to all servers
- for i in ${servers[@]}; do
- ./hey -n 85000 -c 85 $i/v2/keys/foo | grep -e "Requests/sec" -e "Latency" -e "90%" | tr "\n" "\t" | xargs echo
- done
-
-done
diff --git a/hack/patch/README.md b/hack/patch/README.md
deleted file mode 100644
index 32323f179965..000000000000
--- a/hack/patch/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# ./hack/patch/cherrypick.sh
-
-Handles cherry-picks of PR(s) from etcd main to a stable etcd release branch automatically.
-
-## Setup
-
-Set the `UPSTREAM_REMOTE` and `FORK_REMOTE` environment variables.
-`UPSTREAM_REMOTE` should be set to git remote name of `github.com/etcd-io/etcd`,
-and `FORK_REMOTE` should be set to the git remote name of the forked etcd
-repo (`github.com/${github-username}/etcd`). Use `git remote -v` to
-look up the git remote names. If etcd has not been forked, create
-one on github.com and register it locally with `git remote add ...`.
-
-
-```
-export UPSTREAM_REMOTE=upstream
-export FORK_REMOTE=origin
-export GITHUB_USER=${github-username}
-```
-
-Next, install hub from https://github.com/github/hub
-
-## Usage
-
-To cherry pick PR 12345 onto release-3.2 and propose is as a PR, run:
-
-```sh
-./hack/patch/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.2 12345
-```
-
-To cherry pick 12345 then 56789 and propose them togther as a single PR, run:
-
-```
-./hack/patch/cherrypick.sh ${UPSTREAM_REMOTE}/release-3.2 12345 56789
-```
-
-
diff --git a/hack/tls-setup/config/ca-csr.json b/hack/tls-setup/config/ca-csr.json
index 78b95159942f..7a0686fda7c3 100644
--- a/hack/tls-setup/config/ca-csr.json
+++ b/hack/tls-setup/config/ca-csr.json
@@ -12,5 +12,8 @@
"ST": "California",
"C": "US"
}
- ]
+ ],
+ "ca": {
+ "expiry": "876000h"
+ }
}
diff --git a/pkg/.gomodguard.yaml b/pkg/.gomodguard.yaml
new file mode 100644
index 000000000000..0f60ed482358
--- /dev/null
+++ b/pkg/.gomodguard.yaml
@@ -0,0 +1,13 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/api/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/server/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/pkg/LICENSE b/pkg/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/pkg/LICENSE
+++ b/pkg/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/pkg/adt/interval_tree.go b/pkg/adt/interval_tree.go
index 3c1c3ea8322a..85542020f592 100644
--- a/pkg/adt/interval_tree.go
+++ b/pkg/adt/interval_tree.go
@@ -434,46 +434,27 @@ func (ivt *intervalTree) createIntervalNode(ivl Interval, val any) *intervalNode
}
}
-// TODO: make this consistent with textbook implementation
-//
-// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.3, p315
-//
-// RB-INSERT(T, z)
-//
-// y = T.nil
-// x = T.root
-//
-// while x ≠ T.nil
-// y = x
-// if z.key < x.key
-// x = x.left
-// else
-// x = x.right
-//
-// z.p = y
-//
-// if y == T.nil
-// T.root = z
-// else if z.key < y.key
-// y.left = z
-// else
-// y.right = z
-//
-// z.left = T.nil
-// z.right = T.nil
-// z.color = RED
-//
-// RB-INSERT-FIXUP(T, z)
-
// Insert adds a node with the given interval into the tree.
+//
+// Cormen "Introduction to Algorithms", Chapter 14 Exercise 14.3.5.
+// The algorithm follows Cormen "Introduction to Algorithms", Chapter 14 Exercise 14.3.5.
+// for modifying an interval tree structure to support exact interval matching.
func (ivt *intervalTree) Insert(ivl Interval, val any) {
y := ivt.sentinel
z := ivt.createIntervalNode(ivl, val)
x := ivt.root
for x != ivt.sentinel {
y = x
- if z.iv.Ivl.Begin.Compare(x.iv.Ivl.Begin) < 0 {
+ // Split on left endpoint. If left endpoints match, instead split on right endpoint.
+ beginCompare := z.iv.Ivl.Begin.Compare(x.iv.Ivl.Begin)
+ if beginCompare < 0 {
x = x.left
+ } else if beginCompare == 0 {
+ if z.iv.Ivl.End.Compare(x.iv.Ivl.End) < 0 {
+ x = x.left
+ } else {
+ x = x.right
+ }
} else {
x = x.right
}
@@ -483,8 +464,15 @@ func (ivt *intervalTree) Insert(ivl Interval, val any) {
if y == ivt.sentinel {
ivt.root = z
} else {
- if z.iv.Ivl.Begin.Compare(y.iv.Ivl.Begin) < 0 {
+ beginCompare := z.iv.Ivl.Begin.Compare(y.iv.Ivl.Begin)
+ if beginCompare < 0 {
y.left = z
+ } else if beginCompare == 0 {
+ if z.iv.Ivl.End.Compare(y.iv.Ivl.End) < 0 {
+ y.left = z
+ } else {
+ y.right = z
+ }
} else {
y.right = z
}
@@ -699,18 +687,34 @@ func (ivt *intervalTree) Visit(ivl Interval, ivv IntervalVisitor) {
ivt.root.visit(&ivl, ivt.sentinel, func(n *intervalNode) bool { return ivv(&n.iv) })
}
-// find the exact node for a given interval
+// find the exact node for a given interval. The implementation follows
+// Cormen "Introduction to Algorithms", Chapter 14 Exercise 14.3.5. for
+// exact interval matching. The search runs in O(log n) time on an n-node
+// interval tree.
func (ivt *intervalTree) find(ivl Interval) *intervalNode {
- ret := ivt.sentinel
- f := func(n *intervalNode) bool {
- if n.iv.Ivl != ivl {
- return true
+ x := ivt.root
+ // Search until hit sentinel or exact match.
+ for x != ivt.sentinel {
+ beginCompare := ivl.Begin.Compare(x.iv.Ivl.Begin)
+ endCompare := ivl.End.Compare(x.iv.Ivl.End)
+ if beginCompare == 0 && endCompare == 0 {
+ return x
+ }
+ // Split on left endpoint. If left endpoints match,
+ // instead split on right endpoints.
+ if beginCompare < 0 {
+ x = x.left
+ } else if beginCompare == 0 {
+ if endCompare < 0 {
+ x = x.left
+ } else {
+ x = x.right
+ }
+ } else {
+ x = x.right
}
- ret = n
- return false
}
- ivt.root.visit(&ivl, ivt.sentinel, f)
- return ret
+ return x
}
// Find gets the IntervalValue for the node matching the given interval
diff --git a/pkg/adt/interval_tree_test.go b/pkg/adt/interval_tree_test.go
index 8eb0246ad971..012e044e0e40 100644
--- a/pkg/adt/interval_tree_test.go
+++ b/pkg/adt/interval_tree_test.go
@@ -269,6 +269,36 @@ func TestIntervalTreeDelete(t *testing.T) {
require.Truef(t, reflect.DeepEqual(expectedAfterDelete11, visitsAfterDelete11), "level order after deleting '11' expected %v, got %v", expectedAfterDelete11, visitsAfterDelete11)
}
+func TestIntervalTreeFind(t *testing.T) {
+ ivt := NewIntervalTree()
+ ivl1 := NewInt64Interval(3, 6)
+ val := 123
+ assert.Nilf(t, ivt.Find(ivl1), "find for %v expected nil on empty tree", ivl1)
+ // insert interval [3, 6) into tree
+ ivt.Insert(ivl1, val)
+ // check cases of expected find matches and non-matches
+ assert.NotNilf(t, ivt.Find(ivl1), "find expected not-nil on exact-matched interval %v", ivl1)
+ assert.Equalf(t, ivl1, ivt.Find(ivl1).Ivl, "find expected to return exact-matched interval %v", ivl1)
+ ivl2 := NewInt64Interval(3, 7)
+ assert.Nilf(t, ivt.Find(ivl2), "find expected nil on matched start, different end %v", ivl2)
+ ivl3 := NewInt64Interval(2, 6)
+ assert.Nilf(t, ivt.Find(ivl3), "find expected nil on different start, matched end %v", ivl3)
+ ivl4 := NewInt64Interval(10, 20)
+ assert.Nilf(t, ivt.Find(ivl4), "find expected nil on different start, different end %v", ivl4)
+ // insert the additional intervals into the tree, and check they can each be found.
+ ivls := []Interval{ivl2, ivl3, ivl4}
+ for _, ivl := range ivls {
+ ivt.Insert(ivl, val)
+ assert.NotNilf(t, ivt.Find(ivl), "find expected not-nil on exact-matched interval %v", ivl)
+ assert.Equalf(t, ivl, ivt.Find(ivl).Ivl, "find expected to return exact-matched interval %v", ivl)
+ }
+ // check additional intervals no longer found after deletion
+ for _, ivl := range ivls {
+ assert.Truef(t, ivt.Delete(ivl), "expected successful delete on %v", ivl)
+ assert.Nilf(t, ivt.Find(ivl), "find expected nil after deleted interval %v", ivl)
+ }
+}
+
func TestIntervalTreeIntersects(t *testing.T) {
ivt := NewIntervalTree()
ivt.Insert(NewStringInterval("1", "3"), 123)
@@ -341,8 +371,14 @@ func TestIntervalTreeRandom(t *testing.T) {
require.NotEmptyf(t, ivt.Stab(NewInt64Point(v)), "expected %v stab non-zero for [%+v)", v, xy)
require.Truef(t, ivt.Intersects(NewInt64Point(v)), "did not get %d as expected for [%+v)", v, xy)
}
- assert.Truef(t, ivt.Delete(NewInt64Interval(ab.x, ab.y)), "did not delete %v as expected", ab)
+ ivl := NewInt64Interval(ab.x, ab.y)
+ iv := ivt.Find(ivl)
+ assert.NotNilf(t, iv, "expected find non-nil on %v", ab)
+ assert.Equalf(t, ivl, iv.Ivl, "find did not get matched interval %v", ab)
+ assert.Truef(t, ivt.Delete(ivl), "did not delete %v as expected", ab)
delete(ivs, ab)
+ ivAfterDel := ivt.Find(ivl)
+ assert.Nilf(t, ivAfterDel, "expected find nil after deletion on %v", ab)
}
assert.Equalf(t, 0, ivt.Len(), "got ivt.Len() = %v, expected 0", ivt.Len())
diff --git a/pkg/cobrautl/help.go b/pkg/cobrautl/help.go
index 574578199e70..d4c4896b3277 100644
--- a/pkg/cobrautl/help.go
+++ b/pkg/cobrautl/help.go
@@ -151,6 +151,8 @@ func getSubCommands(cmd *cobra.Command) []*cobra.Command {
return subCommands
}
+// UsageFunc is the usage function for the cobra command.
+// Deprecated: Please use go.etcd.io/etcd/etcdctl/v3/util instead.
func UsageFunc(cmd *cobra.Command, version, APIVersion string) error {
subCommands := getSubCommands(cmd)
tabOut := getTabOutWithWriter(os.Stdout)
diff --git a/pkg/cpuutil/endian.go b/pkg/cpuutil/endian.go
index d654b747664d..ab54d120c772 100644
--- a/pkg/cpuutil/endian.go
+++ b/pkg/cpuutil/endian.go
@@ -16,21 +16,14 @@ package cpuutil
import (
"encoding/binary"
- "unsafe"
-)
-
-const intWidth = int(unsafe.Sizeof(0))
-var byteOrder binary.ByteOrder
+ "golang.org/x/sys/cpu"
+)
// ByteOrder returns the byte order for the CPU's native endianness.
-func ByteOrder() binary.ByteOrder { return byteOrder }
-
-func init() {
- i := 0x1
- if v := (*[intWidth]byte)(unsafe.Pointer(&i)); v[0] == 0 {
- byteOrder = binary.BigEndian
- } else {
- byteOrder = binary.LittleEndian
+func ByteOrder() binary.ByteOrder {
+ if cpu.IsBigEndian {
+ return binary.BigEndian
}
+ return binary.LittleEndian
}
diff --git a/pkg/crc/crc.go b/pkg/crc/crc.go
index 4b998a48455f..dc4a19d86e3b 100644
--- a/pkg/crc/crc.go
+++ b/pkg/crc/crc.go
@@ -1,6 +1,16 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 crc provides utility function for cyclic redundancy check
// algorithms.
diff --git a/pkg/crc/crc_test.go b/pkg/crc/crc_test.go
index 38990fac68ce..732b343ee04b 100644
--- a/pkg/crc/crc_test.go
+++ b/pkg/crc/crc_test.go
@@ -1,6 +1,16 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 crc
diff --git a/pkg/expect/expect.go b/pkg/expect/expect.go
index 8aab16e52a4e..2904e4bc98ff 100644
--- a/pkg/expect/expect.go
+++ b/pkg/expect/expect.go
@@ -198,7 +198,7 @@ func (ep *ExpectProcess) ExpectFunc(ctx context.Context, f func(string) bool) (s
}
select {
- // NOTE: we wait readCloseCh for ep.read() to complete draining the log before acquring the lock.
+ // NOTE: we wait readCloseCh for ep.read() to complete draining the log before acquiring the lock.
case <-ep.readCloseCh:
case <-ctx.Done():
return "", fmt.Errorf("context done before to found matching log")
diff --git a/pkg/expect/expect_test.go b/pkg/expect/expect_test.go
index 3d621b9aacbd..95862ddc5500 100644
--- a/pkg/expect/expect_test.go
+++ b/pkg/expect/expect_test.go
@@ -31,7 +31,7 @@ func TestExpectFunc(t *testing.T) {
ep, err := NewExpect("echo", "hello world")
require.NoError(t, err)
wstr := "hello world\r\n"
- l, eerr := ep.ExpectFunc(context.Background(), func(a string) bool { return len(a) > 10 })
+ l, eerr := ep.ExpectFunc(t.Context(), func(a string) bool { return len(a) > 10 })
require.NoError(t, eerr)
require.Equalf(t, l, wstr, `got "%v", expected "%v"`, l, wstr)
require.NoError(t, ep.Close())
@@ -49,7 +49,7 @@ func TestExpectFuncTimeout(t *testing.T) {
}
}()
- ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 500*time.Millisecond)
defer cancel()
_, err = ep.ExpectFunc(ctx, func(a string) bool { return false })
@@ -66,7 +66,7 @@ func TestExpectFuncExitFailure(t *testing.T) {
ep, err := NewExpect("tail", "-x")
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 500*time.Millisecond)
defer cancel()
_, err = ep.ExpectFunc(ctx, func(s string) bool {
@@ -81,7 +81,7 @@ func TestExpectFuncExitFailureStop(t *testing.T) {
ep, err := NewExpect("tail", "-x")
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 500*time.Millisecond)
defer cancel()
_, err = ep.ExpectFunc(ctx, func(s string) bool {
@@ -101,7 +101,7 @@ func TestExpectFuncExitFailureStop(t *testing.T) {
func TestEcho(t *testing.T) {
ep, err := NewExpect("echo", "hello world")
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
l, eerr := ep.ExpectWithContext(ctx, ExpectedResponse{Value: "world"})
require.NoError(t, eerr)
wstr := "hello world"
@@ -115,7 +115,7 @@ func TestLineCount(t *testing.T) {
ep, err := NewExpect("printf", "1\n2\n3")
require.NoError(t, err)
wstr := "3"
- l, eerr := ep.ExpectWithContext(context.Background(), ExpectedResponse{Value: wstr})
+ l, eerr := ep.ExpectWithContext(t.Context(), ExpectedResponse{Value: wstr})
require.NoError(t, eerr)
require.Equalf(t, l, wstr, `got "%v", expected "%v"`, l, wstr)
require.Equalf(t, 3, ep.LineCount(), "got %d, expected 3", ep.LineCount())
@@ -127,7 +127,7 @@ func TestSend(t *testing.T) {
require.NoError(t, err)
err = ep.Send("a\r")
require.NoError(t, err)
- _, err = ep.ExpectWithContext(context.Background(), ExpectedResponse{Value: "b"})
+ _, err = ep.ExpectWithContext(t.Context(), ExpectedResponse{Value: "b"})
require.NoError(t, err)
require.NoError(t, ep.Stop())
}
@@ -208,7 +208,7 @@ func TestResponseMatchRegularExpr(t *testing.T) {
ep, err := NewExpect("echo", "-n", tc.mockOutput)
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
l, err := ep.ExpectWithContext(ctx, tc.expectedResp)
diff --git a/pkg/go.mod b/pkg/go.mod
index 28440c9a69e8..3e017081361b 100644
--- a/pkg/go.mod
+++ b/pkg/go.mod
@@ -1,8 +1,8 @@
module go.etcd.io/etcd/pkg/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/creack/pty v1.1.18
@@ -10,34 +10,26 @@ require (
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/stretchr/testify v1.11.1
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.uber.org/zap v1.27.0
- google.golang.org/grpc v1.79.3
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.opentelemetry.io/otel/trace v1.43.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/sys v0.45.0
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
)
require (
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ go.opentelemetry.io/otel v1.43.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/protobuf v1.36.10 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace go.etcd.io/etcd/client/pkg/v3 => ../client/pkg
-
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-// Etcd contains lots of packages and dependency relationship.
-// Shouldn't import unnecessary dependencies
-replace (
- go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/api/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/tests/v3 => ./FORBIDDEN_DEPENDENCY
- go.etcd.io/etcd/v3 => ./FORBIDDEN_DEPENDENCY
-)
diff --git a/pkg/go.sum b/pkg/go.sum
index 9c74a87ffd08..4e82fad30920 100644
--- a/pkg/go.sum
+++ b/pkg/go.sum
@@ -1,19 +1,18 @@
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -26,8 +25,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -40,37 +39,37 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/pkg/grpctesting/stub_server.go b/pkg/grpctesting/stub_server.go
index bdc991cbed20..fbd7e3f4ae81 100644
--- a/pkg/grpctesting/stub_server.go
+++ b/pkg/grpctesting/stub_server.go
@@ -25,7 +25,7 @@ import (
testpb "google.golang.org/grpc/interop/grpc_testing"
)
-// StubServer is borrowed from the interal package of grpc-go.
+// StubServer is borrowed from the internal package of grpc-go.
// See https://github.com/grpc/grpc-go/blob/master/internal/stubserver/stubserver.go
// Since it cannot be imported directly, we have to copy and paste it here,
// and useless code for our testing is removed.
diff --git a/pkg/idutil/id.go b/pkg/idutil/id.go
index 63a02cd73461..b185b1a58e1f 100644
--- a/pkg/idutil/id.go
+++ b/pkg/idutil/id.go
@@ -43,7 +43,7 @@ const (
//
// It increases suffix to generate the next id.
// The count field may overflow to timestamp field, which is intentional.
-// It helps to extend the event window to 2^56. This doesn't break that
+// It helps to extend the event window to 2^48. This doesn't break that
// id generated after restart is unique because etcd throughput is <<
// 256req/ms(250k reqs/second).
type Generator struct {
diff --git a/pkg/netutil/netutil_test.go b/pkg/netutil/netutil_test.go
index 47a9a4df0988..f3953caed94f 100644
--- a/pkg/netutil/netutil_test.go
+++ b/pkg/netutil/netutil_test.go
@@ -134,7 +134,7 @@ func TestResolveTCPAddrs(t *testing.T) {
}
return &net.TCPAddr{IP: net.ParseIP(tt.hostMap[host]), Port: i, Zone: ""}, nil
}
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
urls, err := resolveTCPAddrs(ctx, zaptest.NewLogger(t), tt.urls)
cancel()
if tt.hasError {
@@ -305,7 +305,7 @@ func TestURLsEqual(t *testing.T) {
}
for i, test := range tests {
- result, err := urlsEqual(context.TODO(), zaptest.NewLogger(t), test.a, test.b)
+ result, err := urlsEqual(t.Context(), zaptest.NewLogger(t), test.a, test.b)
assert.Equalf(t, result, test.expect, "idx=%d #%d: a:%v b:%v, expected %v but %v", i, test.n, test.a, test.b, test.expect, result)
if test.err != nil {
if err.Error() != test.err.Error() {
@@ -342,7 +342,7 @@ func TestURLStringsEqual(t *testing.T) {
for idx, c := range cases {
t.Logf("TestURLStringsEqual, case #%d", idx)
resolveTCPAddr = c.resolver
- result, err := URLStringsEqual(context.TODO(), zaptest.NewLogger(t), c.urlsA, c.urlsB)
+ result, err := URLStringsEqual(t.Context(), zaptest.NewLogger(t), c.urlsA, c.urlsB)
assert.Truef(t, result, "unexpected result %v", result)
assert.NoErrorf(t, err, "unexpected error %v", err)
}
diff --git a/pkg/osutil/interrupt_unix.go b/pkg/osutil/interrupt_unix.go
index ec9dc7b67a60..64b270e3c8db 100644
--- a/pkg/osutil/interrupt_unix.go
+++ b/pkg/osutil/interrupt_unix.go
@@ -22,9 +22,9 @@ import (
"sync"
"syscall"
- "go.etcd.io/etcd/client/pkg/v3/verify"
-
"go.uber.org/zap"
+
+ "go.etcd.io/etcd/client/pkg/v3/verify"
)
// InterruptHandler is a function that is called on receiving a
diff --git a/pkg/pbutil/pbutil.go b/pkg/pbutil/pbutil.go
index 821f59703ae6..682eb8971791 100644
--- a/pkg/pbutil/pbutil.go
+++ b/pkg/pbutil/pbutil.go
@@ -15,7 +15,11 @@
// Package pbutil defines interfaces for handling Protocol Buffer objects.
package pbutil
-import "fmt"
+import (
+ "fmt"
+
+ "google.golang.org/protobuf/proto"
+)
type Marshaler interface {
Marshal() (data []byte, err error)
@@ -46,6 +50,20 @@ func MaybeUnmarshal(um Unmarshaler, data []byte) bool {
return true
}
+func MustMarshalMessage(m proto.Message) []byte {
+ d, err := proto.Marshal(m)
+ if err != nil {
+ panic(fmt.Sprintf("marshal should never fail (%v)", err))
+ }
+ return d
+}
+
+func MustUnmarshalMessage(um proto.Message, data []byte) {
+ if err := proto.Unmarshal(data, um); err != nil {
+ panic(fmt.Sprintf("unmarshal should never fail (%v)", err))
+ }
+}
+
func GetBool(v *bool) (vv bool, set bool) {
if v == nil {
return false, false
diff --git a/pkg/pbutil/pbutil_test.go b/pkg/pbutil/pbutil_test.go
index ab6edcdc035d..9404af2704a0 100644
--- a/pkg/pbutil/pbutil_test.go
+++ b/pkg/pbutil/pbutil_test.go
@@ -20,6 +20,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/known/wrapperspb"
)
func TestMarshaler(t *testing.T) {
@@ -52,6 +55,29 @@ func TestUnmarshalerPanic(t *testing.T) {
MustUnmarshal(m, nil)
}
+func TestProtoMarshaler(t *testing.T) {
+ m := wrapperspb.Int64(7)
+ g := MustMarshalMessage(m)
+ w, err := proto.Marshal(m)
+ require.NoError(t, err)
+ assert.Equal(t, w, g)
+}
+
+func TestProtoUnmarshaler(t *testing.T) {
+ data := MustMarshalMessage(wrapperspb.Int64(9))
+ m := &wrapperspb.Int64Value{}
+ MustUnmarshalMessage(m, data)
+ assert.Equal(t, int64(9), m.Value)
+}
+
+func TestProtoUnmarshalerPanic(t *testing.T) {
+ defer func() {
+ assert.NotNilf(t, recover(), "recover = nil, want error")
+ }()
+ m := &wrapperspb.Int64Value{}
+ MustUnmarshalMessage(m, []byte("not-a-protobuf"))
+}
+
func TestGetBool(t *testing.T) {
tests := []struct {
b *bool
diff --git a/pkg/proxy/server_test.go b/pkg/proxy/server_test.go
index 2739c76858c0..2a7e622a558f 100644
--- a/pkg/proxy/server_test.go
+++ b/pkg/proxy/server_test.go
@@ -16,7 +16,6 @@ package proxy
import (
"bytes"
- "context"
"crypto/tls"
"fmt"
"io"
@@ -613,7 +612,7 @@ func send(t *testing.T, data []byte, scheme, addr string, tlsInfo transport.TLSI
if !tlsInfo.Empty() {
tp, terr := transport.NewTransport(tlsInfo, 3*time.Second)
require.NoError(t, terr)
- out, err = tp.DialContext(context.Background(), scheme, addr)
+ out, err = tp.DialContext(t.Context(), scheme, addr)
} else {
out, err = net.Dial(scheme, addr)
}
diff --git a/pkg/report/perfdash.go b/pkg/report/perfdash.go
new file mode 100644
index 000000000000..794d0f0878e3
--- /dev/null
+++ b/pkg/report/perfdash.go
@@ -0,0 +1,88 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 report
+
+import (
+ "encoding/json"
+ "fmt"
+ "math"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+type Metrics struct {
+ Perc50 float64 `json:"Perc50"`
+ Perc90 float64 `json:"Perc90"`
+ Perc99 float64 `json:"Perc99"`
+}
+
+type Labels struct {
+ Operation string `json:"Operation"`
+}
+
+type DataItem struct {
+ Data Metrics `json:"data"`
+ Labels Labels `json:"labels"`
+ Unit string `json:"unit"`
+}
+
+type perfdashFormattedReport struct {
+ Version string `json:"version"`
+ DataItems []DataItem `json:"dataItems"`
+}
+
+func (r *report) writePerfDashReport(benchmarkOp string) {
+ pcls, data := Percentiles(r.stats.Lats)
+ pclsData := make(map[float64]float64)
+ for i := 0; i < len(pcls); i++ {
+ pclsData[pcls[i]] = data[i] * 1000 // Since the reported data is in seconds, convert to ms.
+ }
+ report := perfdashFormattedReport{
+ Version: "v1",
+ DataItems: []DataItem{
+ {
+ Data: Metrics{
+ Perc50: math.Round(pclsData[50]*10000) / 10000,
+ Perc90: math.Round(pclsData[90]*10000) / 10000,
+ Perc99: math.Round(pclsData[99]*10000) / 10000,
+ },
+ Unit: "ms",
+ Labels: Labels{
+ Operation: strings.ToUpper(benchmarkOp),
+ },
+ },
+ },
+ }
+ reportB, _ := json.MarshalIndent(report, "", " ")
+
+ artifactsDir := os.Getenv("ARTIFACTS")
+ if artifactsDir == "" {
+ artifactsDir = "./_artifacts"
+ }
+
+ fileName := fmt.Sprintf("EtcdAPI_benchmark_%s_%s.json", benchmarkOp, time.Now().UTC().Format(time.RFC3339))
+ err := os.MkdirAll(artifactsDir, 0o755)
+ if err != nil {
+ fmt.Println("Error creating artifacts directory:", err)
+ }
+ destPath := filepath.Join(artifactsDir, fileName)
+ err = os.WriteFile(destPath, reportB, 0o644)
+ if err != nil {
+ fmt.Println("Error writing to file:", err)
+ }
+ fmt.Println("Successfully created a JSON perf report at", destPath)
+}
diff --git a/pkg/report/report.go b/pkg/report/report.go
index a33f97cfa9d9..657018ed424d 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -41,8 +41,10 @@ type Result struct {
func (res *Result) Duration() time.Duration { return res.End.Sub(res.Start) }
type report struct {
- results chan Result
- precision string
+ generatePerfReport bool
+ benchmarkOp string
+ precision string
+ results chan Result
stats Stats
sps *secondPoints
@@ -81,19 +83,23 @@ type Report interface {
Stats() <-chan Stats
}
-func NewReport(precision string) Report { return newReport(precision) }
+func NewReport(precision, benchmarkOp string, generatePerfReport bool) Report {
+ return newReport(precision, benchmarkOp, generatePerfReport)
+}
-func newReport(precision string) *report {
+func newReport(precision, benchmarkOp string, generatePerfReport bool) *report {
r := &report{
- results: make(chan Result, 16),
- precision: precision,
+ results: make(chan Result, 16),
+ precision: precision,
+ generatePerfReport: generatePerfReport,
+ benchmarkOp: benchmarkOp,
}
r.stats.ErrorDist = make(map[string]int)
return r
}
-func NewReportSample(precision string) Report {
- r := NewReport(precision).(*report)
+func NewReportSample(precision, benchmarkOp string, generatePerfReport bool) Report {
+ r := NewReport(precision, benchmarkOp, generatePerfReport).(*report)
r.sps = newSecondPoints()
return r
}
@@ -105,6 +111,9 @@ func (r *report) Run() <-chan string {
go func() {
defer close(donec)
r.processResults()
+ if r.generatePerfReport {
+ r.writePerfDashReport(r.benchmarkOp)
+ }
donec <- r.String()
}()
return donec
@@ -155,8 +164,8 @@ func (r *report) sec2str(sec float64) string { return fmt.Sprintf(r.precision+"
type reportRate struct{ *report }
-func NewReportRate(precision string) Report {
- return &reportRate{NewReport(precision).(*report)}
+func NewReportRate(precision, benchmarkOp string, generatePerfReport bool) Report {
+ return &reportRate{NewReport(precision, benchmarkOp, generatePerfReport).(*report)}
}
func (r *reportRate) String() string {
diff --git a/pkg/report/report_test.go b/pkg/report/report_test.go
index 0c9e370289ba..1d5a49e073c0 100644
--- a/pkg/report/report_test.go
+++ b/pkg/report/report_test.go
@@ -37,7 +37,7 @@ func TestPercentiles(t *testing.T) {
}
func TestReport(t *testing.T) {
- r := NewReportSample("%f")
+ r := NewReportSample("%f", "", false)
go func() {
start := time.Now()
for i := 0; i < 5; i++ {
@@ -77,7 +77,7 @@ func TestReport(t *testing.T) {
}
func TestWeightedReport(t *testing.T) {
- r := NewWeightedReport(NewReport("%f"), "%f")
+ r := NewWeightedReport(NewReport("%f", "", false), "%f", "", false)
go func() {
start := time.Now()
for i := 0; i < 5; i++ {
diff --git a/pkg/report/weighted.go b/pkg/report/weighted.go
index 411214f6d165..efaa9af7446f 100644
--- a/pkg/report/weighted.go
+++ b/pkg/report/weighted.go
@@ -30,10 +30,10 @@ type weightedReport struct {
// NewWeightedReport returns a report that includes
// both weighted and unweighted statistics.
-func NewWeightedReport(r Report, precision string) Report {
+func NewWeightedReport(r Report, precision, benchmarkOp string, generatePerfReport bool) Report {
return &weightedReport{
baseReport: r,
- report: newReport(precision),
+ report: newReport(precision, benchmarkOp, generatePerfReport),
results: make(chan Result, 16),
}
}
diff --git a/pkg/traceutil/trace.go b/pkg/traceutil/trace.go
index abf5cf1d229a..121add9b9b0a 100644
--- a/pkg/traceutil/trace.go
+++ b/pkg/traceutil/trace.go
@@ -22,9 +22,19 @@ import (
"strings"
"time"
+ "go.opentelemetry.io/otel/trace"
+ "go.opentelemetry.io/otel/trace/noop"
"go.uber.org/zap"
)
+const instrumentationScope = "go.etcd.io/etcd"
+
+var Tracer trace.Tracer = noop.NewTracerProvider().Tracer(instrumentationScope)
+
+func Init(tp trace.TracerProvider) {
+ Tracer = tp.Tracer(instrumentationScope)
+}
+
// TraceKey is used as a key of context for Trace.
type TraceKey struct{}
@@ -72,7 +82,7 @@ type step struct {
isSubTraceEnd bool
}
-func New(op string, lg *zap.Logger, fields ...Field) *Trace {
+func newTrace(op string, lg *zap.Logger, fields ...Field) *Trace {
return &Trace{operation: op, lg: lg, startTime: time.Now(), fields: fields}
}
@@ -88,6 +98,19 @@ func Get(ctx context.Context) *Trace {
return TODO()
}
+// EnsureTrace creates a new trace if needed and adds it to the context.
+func EnsureTrace(ctx context.Context, lg *zap.Logger, operation string, fields ...Field) (context.Context, *Trace) {
+ trace := Get(ctx)
+ if trace.IsEmpty() {
+ trace = newTrace(operation,
+ lg,
+ fields...,
+ )
+ ctx = context.WithValue(ctx, TraceKey{}, trace)
+ }
+ return ctx, trace
+}
+
func (t *Trace) GetStartTime() time.Time {
return t.startTime
}
@@ -177,7 +200,6 @@ func (t *Trace) logInfo(threshold time.Duration) (string, []zap.Field) {
endTime := time.Now()
totalDuration := endTime.Sub(t.startTime)
traceNum := rand.Int31()
- msg := fmt.Sprintf("trace[%d] %s", traceNum, t.operation)
var steps []string
lastStepTime := t.startTime
@@ -205,13 +227,15 @@ func (t *Trace) logInfo(threshold time.Duration) (string, []zap.Field) {
}
stepDuration := tstep.time.Sub(lastStepTime)
if stepDuration > threshold {
- steps = append(steps, fmt.Sprintf("trace[%d] '%v' %s (duration: %v)",
- traceNum, tstep.msg, writeFields(tstep.fields), stepDuration))
+ steps = append(steps, fmt.Sprintf("'%v' %s (duration: %v)",
+ tstep.msg, writeFields(tstep.fields), stepDuration))
}
lastStepTime = tstep.time
}
fs := []zap.Field{
+ zap.Int32("trace_id", traceNum),
+ zap.String("operation", t.operation),
zap.String("detail", writeFields(t.fields)),
zap.Duration("duration", totalDuration),
zap.Time("start", t.startTime),
@@ -219,7 +243,7 @@ func (t *Trace) logInfo(threshold time.Duration) (string, []zap.Field) {
zap.Strings("steps", steps),
zap.Int("step_count", len(steps)),
}
- return msg, fs
+ return "trace", fs
}
func (t *Trace) updateFieldIfExist(f Field) bool {
diff --git a/pkg/traceutil/trace_test.go b/pkg/traceutil/trace_test.go
index 7b4ae28e61ce..10f8b2b192a1 100644
--- a/pkg/traceutil/trace_test.go
+++ b/pkg/traceutil/trace_test.go
@@ -38,12 +38,12 @@ func TestGet(t *testing.T) {
}{
{
name: "When the context does not have trace",
- inputCtx: context.TODO(),
+ inputCtx: t.Context(),
outputTrace: TODO(),
},
{
name: "When the context has trace",
- inputCtx: context.WithValue(context.Background(), TraceKey{}, traceForTest),
+ inputCtx: context.WithValue(t.Context(), TraceKey{}, traceForTest),
outputTrace: traceForTest,
},
}
@@ -73,7 +73,7 @@ func TestCreate(t *testing.T) {
}
)
- trace := New(op, nil, fields[0], fields[1])
+ _, trace := EnsureTrace(t.Context(), nil, op, fields[0], fields[1])
assert.Equalf(t, trace.operation, op, "Expected %v; Got %v", op, trace.operation)
for i, f := range trace.fields {
assert.Equalf(t, f.Key, fields[i].Key, "Expected %v; Got %v", fields[i].Key, f.Key)
@@ -93,10 +93,11 @@ func TestCreate(t *testing.T) {
func TestLog(t *testing.T) {
tests := []struct {
- name string
- trace *Trace
- fields []Field
- expectedMsg []string
+ name string
+ trace *Trace
+ fields []Field
+ expectedMsg []string
+ notExpectedMsg []string
}{
{
name: "When dump all logs",
@@ -135,12 +136,18 @@ func TestLog(t *testing.T) {
{"count", 1},
},
expectedMsg: []string{
- "Test",
+ // stable message name
+ "\"trace\"",
+ // operation and trace_id emitted as structured fields
+ "\"operation\":\"Test\"",
+ "\"trace_id\":",
"msg1", "msg2",
"traceKey1:traceValue1", "count:1",
"stepKey1:stepValue1", "stepKey2:stepValue2",
"\"step_count\":2",
},
+ // step entries must not embed the trace ID inline
+ notExpectedMsg: []string{"trace["},
},
{
name: "When trace has subtrace",
@@ -178,13 +185,16 @@ func TestLog(t *testing.T) {
{"count", 1},
},
expectedMsg: []string{
- "Test",
+ "\"trace\"",
+ "\"operation\":\"Test\"",
+ "\"trace_id\":",
"msg1", "msg2", "submsg",
"traceKey1:traceValue1", "count:1",
"stepKey1:stepValue1", "stepKey2:stepValue2", "subStepKey:subStepValue",
"beginSubTrace:true", "endSubTrace:true",
"\"step_count\":3",
},
+ notExpectedMsg: []string{"trace["},
},
}
@@ -209,6 +219,9 @@ func TestLog(t *testing.T) {
for _, msg := range tt.expectedMsg {
assert.Truef(t, bytes.Contains(data, []byte(msg)), "Expected to find %v in log", msg)
}
+ for _, msg := range tt.notExpectedMsg {
+ assert.Falsef(t, bytes.Contains(data, []byte(msg)), "Expected NOT to find %v in log", msg)
+ }
})
}
}
diff --git a/scripts/benchmark_test.sh b/scripts/benchmark_test.sh
new file mode 100755
index 000000000000..bff071fcafd7
--- /dev/null
+++ b/scripts/benchmark_test.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+# This script runs a benchmark on a locally started etcd server
+
+set -euo pipefail
+
+source ./scripts/test_lib.sh
+
+COMMON_BENCHMARK_FLAGS="--report-perfdash"
+
+if [[ $# -lt 1 ]]; then
+ echo "Usage: $0 [tester args...]"
+ exit 1
+fi
+
+BENCHMARK_NAME="$1"
+ARGS="${*:2}"
+
+echo "Starting the etcd server..."
+
+# Create a directory for etcd data under /tmp/etcd
+mkdir -p /tmp/etcd
+DATA_DIR=$(mktemp -d /tmp/etcd/data-XXXXXX)
+./bin/etcd --data-dir="$DATA_DIR" > /tmp/etcd.log 2>&1 &
+etcd_pid=$!
+
+trap 'log_warning -e "Stopping etcd server - PID $etcd_pid";
+ kill $etcd_pid 2>/dev/null;
+ rm -rf $DATA_DIR;
+ log_success "Deleted the contents from $DATA_DIR related to benchmark test"' EXIT
+
+# Wait until etcd becomes healthy
+for retry in {1..10}; do
+ if ./bin/etcdctl endpoint health --cluster> /dev/null 2>&1; then
+ log_success -e "\\netcd is healthy"
+ break
+ fi
+ log_warning -e "\\nWaiting for etcd to be healthy..."
+ sleep 1
+ if [[ $retry -eq 10 ]]; then
+ log_error -e "\\nFailed to confirm etcd health after $retry attempts. Check /tmp/etcd.log for more information"
+ exit 1
+ fi
+done
+
+log_success -e "etcd process is running with PID $etcd_pid"
+
+log_callout -e "\\nPerforming benchmark $BENCHMARK_NAME with arguments: $ARGS"
+read -r -a TESTER_OPTIONS <<< "$ARGS"
+log_callout "Running: benchmark $BENCHMARK_NAME ${TESTER_OPTIONS[*]} $COMMON_BENCHMARK_FLAGS"
+benchmark "$BENCHMARK_NAME" "${TESTER_OPTIONS[@]}" $COMMON_BENCHMARK_FLAGS
+log_callout "Completed: benchmark $BENCHMARK_NAME ${TESTER_OPTIONS[*]} $COMMON_BENCHMARK_FLAGS"
diff --git a/scripts/build-binary.sh b/scripts/build-binary.sh
index 174c72db4670..d426de4568a5 100755
--- a/scripts/build-binary.sh
+++ b/scripts/build-binary.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh
index 6f86fb46a755..3b432b2b9d5f 100755
--- a/scripts/build-docker.sh
+++ b/scripts/build-docker.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
diff --git a/scripts/build-release.sh b/scripts/build-release.sh
index 4cf1116ee006..d4301a43c1fa 100755
--- a/scripts/build-release.sh
+++ b/scripts/build-release.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
#
# Build all release binaries and images to directory ./release.
# Run from repository root.
diff --git a/scripts/build.sh b/scripts/build.sh
index 15720193a1d4..6e314fcb8a25 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
# This scripts build the etcd binaries
# To build the tools, run `build_tools.sh`
diff --git a/scripts/build_lib.sh b/scripts/build_lib.sh
index bd6279c8ae65..d005890cc3ff 100755
--- a/scripts/build_lib.sh
+++ b/scripts/build_lib.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
@@ -18,6 +31,7 @@ CGO_ENABLED="${CGO_ENABLED:-0}"
# Set GO_LDFLAGS="-s" for building without symbols for debugging.
# shellcheck disable=SC2206
GO_LDFLAGS=(${GO_LDFLAGS:-} "-X=${VERSION_SYMBOL}=${GIT_SHA}")
+GO_GCFLAGS=${GO_GCFLAGS:-}
GO_BUILD_ENV=("CGO_ENABLED=${CGO_ENABLED}" "GO_BUILD_FLAGS=${GO_BUILD_FLAGS}" "GOOS=${GOOS}" "GOARCH=${GOARCH}")
etcd_build() {
@@ -33,6 +47,7 @@ etcd_build() {
-trimpath \
-installsuffix=cgo \
"-ldflags=${GO_LDFLAGS[*]}" \
+ -gcflags="${GO_GCFLAGS}" \
-o="../${out}/etcd" . || return 2
) || return 2
@@ -44,6 +59,7 @@ etcd_build() {
-trimpath \
-installsuffix=cgo \
"-ldflags=${GO_LDFLAGS[*]}" \
+ -gcflags="${GO_GCFLAGS}" \
-o="../${out}/etcdutl" . || return 2
) || return 2
@@ -55,6 +71,7 @@ etcd_build() {
-trimpath \
-installsuffix=cgo \
"-ldflags=${GO_LDFLAGS[*]}" \
+ -gcflags="${GO_GCFLAGS}" \
-o="../${out}/etcdctl" . || return 2
) || return 2
# Verify whether symbol we overwrote exists
diff --git a/scripts/build_tools.sh b/scripts/build_tools.sh
index 7145126d890c..4f5ec2cfa4fd 100755
--- a/scripts/build_tools.sh
+++ b/scripts/build_tools.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
diff --git a/hack/patch/cherrypick.sh b/scripts/cherrypick.sh
similarity index 98%
rename from hack/patch/cherrypick.sh
rename to scripts/cherrypick.sh
index ad143514f553..aee52959b204 100755
--- a/hack/patch/cherrypick.sh
+++ b/scripts/cherrypick.sh
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
+# shellcheck disable=SC2155,SC2128,SC2086
# Based on github.com/kubernetes/kubernetes/blob/v1.8.2/hack/cherry_pick_pull.sh
@@ -26,7 +27,7 @@ if [[ -z ${GITHUB_USER:-} ]]; then
exit 1
fi
-if ! which hub > /dev/null; then
+if ! command -v hub > /dev/null; then
echo "Can't find 'hub' tool in PATH, please install from https://github.com/github/hub"
exit 1
fi
diff --git a/scripts/codecov_upload.sh b/scripts/codecov_upload.sh
index da718775a1f3..fc8a8de8e45b 100755
--- a/scripts/codecov_upload.sh
+++ b/scripts/codecov_upload.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
# Script used to collect and upload test coverage.
@@ -10,7 +23,7 @@ curl -sf -o ./bin/codecov.sh https://codecov.io/bash
bash ./bin/codecov.sh -f "${COVERDIR}/all.coverprofile" \
-cF all \
- -C "${PULL_PULL_SHA}" \
+ -C "${PULL_PULL_SHA:-${PULL_BASE_SHA}}" \
-r "${REPO_OWNER}/${REPO_NAME}" \
-P "${PULL_NUMBER}" \
-b "${BUILD_ID}" \
diff --git a/scripts/etcd_version_annotations.txt b/scripts/etcd_version_annotations.txt
index 48071fb66d1d..c700c58d658e 100644
--- a/scripts/etcd_version_annotations.txt
+++ b/scripts/etcd_version_annotations.txt
@@ -214,7 +214,6 @@ etcdserverpb.InternalRaftRequest.lease_revoke: ""
etcdserverpb.InternalRaftRequest.put: ""
etcdserverpb.InternalRaftRequest.range: ""
etcdserverpb.InternalRaftRequest.txn: ""
-etcdserverpb.InternalRaftRequest.v2: ""
etcdserverpb.LeaseCheckpoint: "3.4"
etcdserverpb.LeaseCheckpoint.ID: ""
etcdserverpb.LeaseCheckpoint.remaining_TTL: ""
@@ -337,24 +336,8 @@ etcdserverpb.RangeResponse.count: ""
etcdserverpb.RangeResponse.header: ""
etcdserverpb.RangeResponse.kvs: ""
etcdserverpb.RangeResponse.more: ""
-etcdserverpb.Request: ""
-etcdserverpb.Request.Dir: ""
-etcdserverpb.Request.Expiration: ""
-etcdserverpb.Request.ID: ""
-etcdserverpb.Request.Method: ""
-etcdserverpb.Request.Path: ""
-etcdserverpb.Request.PrevExist: ""
-etcdserverpb.Request.PrevIndex: ""
-etcdserverpb.Request.PrevValue: ""
-etcdserverpb.Request.Quorum: ""
-etcdserverpb.Request.Recursive: ""
-etcdserverpb.Request.Refresh: ""
-etcdserverpb.Request.Since: ""
-etcdserverpb.Request.Sorted: ""
-etcdserverpb.Request.Stream: ""
-etcdserverpb.Request.Time: ""
-etcdserverpb.Request.Val: ""
-etcdserverpb.Request.Wait: ""
+etcdserverpb.RangeStreamResponse: "3.7"
+etcdserverpb.RangeStreamResponse.range_response: ""
etcdserverpb.RequestHeader: "3.0"
etcdserverpb.RequestHeader.ID: ""
etcdserverpb.RequestHeader.auth_revision: "3.1"
diff --git a/scripts/fix.sh b/scripts/fix.sh
deleted file mode 100755
index 91e3018bc15e..000000000000
--- a/scripts/fix.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env bash
-
-set -euo pipefail
-
-# Top level problems with modules can lead to test_lib being not functional
-go mod tidy
-
-source ./scripts/test_lib.sh
-source ./scripts/updatebom.sh
-
-# To fix according to newer version of go:
-# go get golang.org/dl/gotip
-# gotip download
-# GO_CMD="gotip"
-GO_CMD="go"
-
-function mod_tidy_fix {
- run rm ./go.sum
- run ${GO_CMD} mod tidy || return 2
-}
-
-function bash_ws_fix {
- TAB=$'\t'
-
- log_callout "Fixing whitespaces in the bash scripts"
- # Makes sure all bash scripts do use ' ' (double space) for indention.
- log_cmd "find ./ -name '*.sh' -print0 | xargs -0 sed -i.bak 's|${TAB}| |g'"
- find ./ -name '*.sh' -print0 | xargs -0 sed -i.bak "s|${TAB}| |g"
- find ./ -name '*.sh.bak' -print0 | xargs -0 rm
-}
-
-log_callout -e "\\nFixing etcd code for you...\n"
-
-run_for_modules mod_tidy_fix || exit 2
-run_for_modules run ${GO_CMD} fmt || exit 2
-bash_ws_fix || exit 2
-
-log_success -e "\\nSUCCESS: etcd code is fixed :)"
diff --git a/scripts/fix/bom.sh b/scripts/fix/bom.sh
new file mode 100755
index 000000000000..423552045090
--- /dev/null
+++ b/scripts/fix/bom.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+# Copyright 2026 The etcd Authors
+#
+# 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.
+
+set -euo pipefail
+
+ETCD_ROOT_DIR=${ETCD_ROOT_DIR:-$(git rev-parse --show-toplevel)}
+source "${ETCD_ROOT_DIR}/scripts/test_lib.sh"
+
+log_callout "Generating bill of materials..."
+
+_bom_modules=()
+load_workspace_relative_modules_for_bom _bom_modules
+
+# Internally license-bill-of-materials tends to modify go.sum
+run cp go.sum go.sum.tmp || exit 2
+run cp go.mod go.mod.tmp || exit 2
+
+# Intentionally run the command once first, so it fetches dependencies. The exit code on the first
+# run in a just cloned repository is always dirty.
+GOOS=linux run_go_tool github.com/appscodelabs/license-bill-of-materials \
+ --override-file ./bill-of-materials.override.json "${_bom_modules[@]}" &>/dev/null || true
+
+# BOM file should be generated for linux. Otherwise running this command on other operating systems such as OSX
+# results in certain dependencies being excluded from the BOM file, such as procfs.
+# For more info, https://github.com/etcd-io/etcd/issues/19665
+output=$(GOOS=linux run_go_tool github.com/appscodelabs/license-bill-of-materials \
+ --override-file ./bill-of-materials.override.json \
+ "${_bom_modules[@]}")
+code="$?"
+
+run cp go.sum.tmp go.sum || exit 2
+run cp go.mod.tmp go.mod || exit 2
+
+if [ "${code}" -ne 0 ]; then
+ log_error -e "license-bill-of-materials (code: ${code}) failed with:\\n${output}"
+ exit 255
+fi
+
+echo "${output}" > bill-of-materials.json
+log_success "bill-of-materials.json generated"
diff --git a/scripts/fix/mod-tidy.sh b/scripts/fix/mod-tidy.sh
new file mode 100755
index 000000000000..7dc3e1434341
--- /dev/null
+++ b/scripts/fix/mod-tidy.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# Copyright 2026 The etcd Authors
+#
+# 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.
+
+set -euo pipefail
+
+ETCD_ROOT_DIR=${ETCD_ROOT_DIR:-$(git rev-parse --show-toplevel)}
+source "${ETCD_ROOT_DIR}/scripts/test_lib.sh"
+
+log_callout "Tidying go.mod files"
+run_for_workspace_modules run sh -c "rm -f ./go.sum && go mod tidy"
+log_success "go.mod files tidied"
diff --git a/scripts/fix/shell_ws.sh b/scripts/fix/shell_ws.sh
new file mode 100755
index 000000000000..b165bf6a73cf
--- /dev/null
+++ b/scripts/fix/shell_ws.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+# Fixes whitespaces in bash scripts.
+set -euo pipefail
+
+ETCD_ROOT_DIR=${ETCD_ROOT_DIR:-$(git rev-parse --show-toplevel)}
+source "${ETCD_ROOT_DIR}/scripts/test_lib.sh"
+
+function main {
+ local TAB=$'\t'
+
+ log_callout "Fixing whitespaces in bash scripts"
+ # Makes sure all bash scripts do use ' ' (double space) for indentation.
+ run find "${ETCD_ROOT_DIR}" -name '*.sh' -exec sed -i.bak "s|${TAB}| |g" {} \;
+ run find "${ETCD_ROOT_DIR}" -name '*.sh.bak' -delete
+}
+
+# only run when called directly, not sourced
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ main
+fi
diff --git a/scripts/fix/sync_go_toolchain_directive.sh b/scripts/fix/sync_go_toolchain_directive.sh
new file mode 100755
index 000000000000..db3a13ede306
--- /dev/null
+++ b/scripts/fix/sync_go_toolchain_directive.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+# This script looks at the version present in the .go-version file and treats
+# that to be the value of the toolchain directive that go should use. It then
+# updates the toolchain directives of all go.mod files to reflect this version.
+#
+# We do this to ensure that .go-version acts as the source of truth for go versions.
+
+set -euo pipefail
+
+ETCD_ROOT_DIR=${ETCD_ROOT_DIR:-$(git rev-parse --show-toplevel)}
+source "${ETCD_ROOT_DIR}/scripts/test_lib.sh"
+
+log_callout "Syncing Go toolchain directive"
+
+TARGET_GO_VERSION="${TARGET_GO_VERSION:-"$(cat "${ETCD_ROOT_DIR}/.go-version")"}"
+run find "${ETCD_ROOT_DIR}" -name 'go.mod' -exec go mod edit -toolchain=go"${TARGET_GO_VERSION}" {} \;
+run go work edit -toolchain=go"${TARGET_GO_VERSION}"
+
+log_success "Go toolchain directive synced"
diff --git a/scripts/fix/yamllint.sh b/scripts/fix/yamllint.sh
new file mode 100755
index 000000000000..e603d356b715
--- /dev/null
+++ b/scripts/fix/yamllint.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+# Fixes linter issues in YAML files.
+
+set -euo pipefail
+
+ETCD_ROOT_DIR=${ETCD_ROOT_DIR:-$(git rev-parse --show-toplevel)}
+source "${ETCD_ROOT_DIR}/scripts/test_lib.sh"
+
+function main {
+ run_go_tool github.com/google/yamlfmt/cmd/yamlfmt \
+ -conf "${ETCD_ROOT_DIR}/tools/.yamlfmt" .
+}
+
+# only run when called directly, not sourced
+if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ main
+fi
diff --git a/scripts/fuzzing.sh b/scripts/fuzzing.sh
index 65ffaa92ab60..6a12519cc530 100755
--- a/scripts/fuzzing.sh
+++ b/scripts/fuzzing.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
diff --git a/scripts/genproto.sh b/scripts/genproto.sh
index 52e94a1bab51..e20191e315a2 100755
--- a/scripts/genproto.sh
+++ b/scripts/genproto.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
#
# Generate all etcd protobuf bindings.
# Run from repository root directory named etcd.
@@ -12,18 +25,29 @@ if ! [[ "$0" =~ scripts/genproto.sh ]]; then
exit 255
fi
+if [ -z "${OS:-}" ]; then
+ OS=$(uname -s | tr '[:upper:]' '[:lower:]')
+fi
+
# Set SED variable
if LANG=C sed --help 2>&1 | grep -q GNU; then
SED="sed"
elif command -v gsed &>/dev/null; then
SED="gsed"
+elif [ "$OS" == "darwin" ]; then
+ echo "You are on Mac, running: brew install gnu-sed"
+ brew install gnu-sed
+ SED="/opt/homebrew/opt/gnu-sed/libexec/gnubin/sed"
else
- echo "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2
+ echo "Failed to find GNU sed as sed or gsed." >&2
exit 1
fi
source ./scripts/test_lib.sh
+PATH=$(pwd)/bin:$(go env GOPATH)/bin:$PATH
+export PATH
+
if [[ $(protoc --version | cut -f2 -d' ') != "3.20.3" ]]; then
echo "Could not find protoc 3.20.3, installing now..."
@@ -38,19 +62,23 @@ if [[ $(protoc --version | cut -f2 -d' ') != "3.20.3" ]]; then
;;
esac
- download_url="https://github.com/protocolbuffers/protobuf/releases/download/v3.20.3/protoc-3.20.3-linux-${file}.zip"
- echo "Running on ${arch}."
+ protoc_download_file="protoc-3.20.3-linux-${file}.zip"
+ if [ "$OS" == "darwin" ]; then
+ # protoc-3.20.3 does not have pre-built binaries for darwin_arm64. Thanks to Rosetta, we could use x86_64 binary.
+ protoc_download_file="protoc-3.20.3-osx-x86_64.zip"
+ fi
+ download_url="https://github.com/protocolbuffers/protobuf/releases/download/v3.20.3/${protoc_download_file}"
+ echo "Running on ${OS} ${arch}. Downloading ${protoc_download_file}"
mkdir -p bin
- wget ${download_url} && unzip -p protoc-3.20.3-linux-${file}.zip bin/protoc > tmpFile && mv tmpFile bin/protoc
- rm protoc-3.20.3-linux-${file}.zip
+ wget ${download_url} && unzip -p ${protoc_download_file} bin/protoc > tmpFile && mv tmpFile bin/protoc
+ rm ${protoc_download_file}
chmod +x bin/protoc
- PATH=$PATH:$(pwd)/bin
- export PATH
echo "Now running: $(protoc --version)"
fi
-GOFAST_BIN=$(tool_get_bin github.com/gogo/protobuf/protoc-gen-gofast)
+GOGEN_BIN=$(tool_get_bin google.golang.org/protobuf/cmd/protoc-gen-go)
+GOGENGRPC_BIN=$(tool_get_bin google.golang.org/grpc/cmd/protoc-gen-go-grpc)
GRPC_GATEWAY_BIN=$(tool_get_bin github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway)
OPENAPIV2_BIN=$(tool_get_bin github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2)
GOGOPROTO_ROOT="$(tool_pkg_dir github.com/gogo/protobuf/proto)/.."
@@ -58,6 +86,13 @@ GRPC_GATEWAY_ROOT="$(tool_pkg_dir github.com/grpc-ecosystem/grpc-gateway/v2/prot
RAFT_ROOT="$(tool_pkg_dir go.etcd.io/raft/v3/raftpb)/.."
GOOGLEAPI_ROOT=$(mktemp -d -t 'googleapi.XXXXX')
+module_mapping_list=(
+ Mraftpb/raft.proto=go.etcd.io/raft/v3/raftpb
+ Mgoogle/protobuf/descriptor.proto=google.golang.org/protobuf/types/descriptorpb
+ Mgoogle/protobuf/struct.proto=google.golang.org/protobuf/types/known/structpb
+)
+module_mappings=$(IFS=$','; echo "${module_mapping_list[*]}" )
+
readonly googleapi_commit=0adf469dcd7822bf5bc058a7b0217f5558a75643
function cleanup_googleapi() {
@@ -80,7 +115,8 @@ download_googleapi
echo
echo "Resolved binary and packages versions:"
-echo " - protoc-gen-gofast: ${GOFAST_BIN}"
+echo " - protoc-gen-go: ${GOGEN_BIN}"
+echo " - protoc-gen-go-grpc: ${GOGENGRPC_BIN}"
echo " - protoc-gen-grpc-gateway: ${GRPC_GATEWAY_BIN}"
echo " - openapiv2: ${OPENAPIV2_BIN}"
echo " - gogoproto-root: ${GOGOPROTO_ROOT}"
@@ -91,14 +127,16 @@ GOGOPROTO_PATH="${GOGOPROTO_ROOT}:${GOGOPROTO_ROOT}/protobuf"
# directories containing protos to be built
DIRS="./server/storage/wal/walpb ./api/etcdserverpb ./server/etcdserver/api/snap/snappb ./api/mvccpb ./server/lease/leasepb ./api/authpb ./server/etcdserver/api/v3lock/v3lockpb ./server/etcdserver/api/v3election/v3electionpb ./api/membershippb ./api/versionpb"
-log_callout -e "\\nRunning gofast (gogo) proto generation..."
+log_callout -e "\\nRunning protoc-gen-go proto generation..."
for dir in ${DIRS}; do
run pushd "${dir}"
- run protoc --gofast_out=plugins=grpc:. -I=".:${GOGOPROTO_PATH}:${ETCD_ROOT_DIR}/..:${RAFT_ROOT}:${ETCD_ROOT_DIR}:${GOOGLEAPI_ROOT}" \
- --gofast_opt=paths=source_relative,Mraftpb/raft.proto=go.etcd.io/raft/v3/raftpb,Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor \
+ run protoc --go_out=. -I=".:${GOGOPROTO_PATH}:${ETCD_ROOT_DIR}/..:${RAFT_ROOT}:${ETCD_ROOT_DIR}:${GOOGLEAPI_ROOT}" \
+ "--go_opt=paths=source_relative,${module_mappings}" \
+ --go-grpc_out=. \
+ "--go-grpc_opt=paths=source_relative,${module_mappings}" \
-I"${GRPC_GATEWAY_ROOT}" \
- --plugin="${GOFAST_BIN}" ./**/*.proto
+ ./**/*.proto
run gofmt -s -w ./**/*.pb.go
run_go_tool "golang.org/x/tools/cmd/goimports" -w ./**/*.pb.go
@@ -118,9 +156,9 @@ for pb in api/etcdserverpb/rpc server/etcdserver/api/v3lock/v3lockpb/v3lock serv
-I"${ETCD_ROOT_DIR}/.." \
-I"${RAFT_ROOT}" \
--grpc-gateway_out=logtostderr=true,paths=source_relative:. \
- --grpc-gateway_opt=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor,Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types \
+ "--grpc-gateway_opt=${module_mappings}" \
--openapiv2_out=json_names_for_fields=false,logtostderr=true:./Documentation/dev-guide/apispec/swagger/. \
- --openapiv2_opt=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor,Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types:. \
+ "--openapiv2_opt=${module_mappings}:." \
--plugin="${OPENAPIV2_BIN}" \
--plugin="${GRPC_GATEWAY_BIN}" \
${pb}.proto
@@ -148,41 +186,6 @@ for pb in api/etcdserverpb/rpc server/etcdserver/api/v3lock/v3lockpb/v3lock serv
Documentation/dev-guide/apispec/swagger/"${swaggerName}".swagger.json
done
-# We only upgraded grpc-gateway from v1 to v2, but keep gogo/protobuf as it's for now.
-# So we have to convert v1 message to v2 message. Once we get rid of gogo/protobuf, and
-# start to depend on protobuf v2, then we can remove this patch.
-#
-# TODO(https://github.com/etcd-io/etcd/issues/14533): Remove the patch below after removal of gogo/protobuf
-for pb in api/etcdserverpb/rpc server/etcdserver/api/v3lock/v3lockpb/v3lock server/etcdserver/api/v3election/v3electionpb/v3election; do
- gwfile="$(dirname ${pb})/gw/$(basename ${pb}).pb.gw.go"
-
- # Changes something like below,
- # import (
- # + protov1 "github.com/golang/protobuf/proto"
- # +
- run ${SED?} -i -E "s|import \(|import \(\n\tprotov1 \"github.com/golang/protobuf/proto\"\n|g" "${gwfile}"
-
- # Changes something like below,
- # - return msg, metadata, err
- # + return protov1.MessageV2(msg), metadata, err
- run ${SED?} -i -E "s|return msg, metadata, err|return protov1.MessageV2\(msg\), metadata, err|g" "${gwfile}"
-
- # Changes something like below,
- # - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
- # + if err := marshaler.NewDecoder(newReader()).Decode(protov1.MessageV2(&protoReq)); err != nil && err != io.EOF {
- run ${SED?} -i -E "s|Decode\(\&protoReq\)|Decode\(protov1\.MessageV2\(\&protoReq\)\)|g" "${gwfile}"
-
- # Changes something like below,
- # - forward_Lease_LeaseKeepAlive_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
- # + forward_Lease_LeaseKeepAlive_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) {
- # + m1, err := resp.Recv()
- # + return protov1.MessageV2(m1), err
- # + }, mux.GetForwardResponseOptions()...)
- run ${SED?} -i -E "s|return resp.Recv\(\)|\n\t\t\tm1, err := resp.Recv\(\)\n\t\t\treturn protov1.MessageV2\(m1\), err\n\t\t|g" "${gwfile}"
-
- run go fmt "${gwfile}"
-done
-
if [ "${1:-}" != "--skip-protodoc" ]; then
log_callout "protodoc is auto-generating grpc API reference documentation..."
diff --git a/scripts/markdown_diff_lint.sh b/scripts/markdown_diff_lint.sh
new file mode 100755
index 000000000000..8d1c0a85b92b
--- /dev/null
+++ b/scripts/markdown_diff_lint.sh
@@ -0,0 +1,110 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
+# This script runs markdownlint-cli2 on changed files.
+# Usage: ./markdown_lint.sh
+
+ETCD_ROOT_DIR=$(git rev-parse --show-toplevel)
+
+# We source ./scripts/test_utils.sh, it sets the log functions and color variables.
+source ./scripts/test_utils.sh
+
+# When we source ./scripts/test_utils.sh, it has the line set -u which treats unset variables as errors.
+# We need to unset the variable to avoid the error.
+set +u -eo pipefail
+
+if ! command markdownlint-cli2 dummy.md &>/dev/null; then
+ log_error "markdownlint-cli2 needs to be installed."
+ log_error "Please refer to https://github.com/DavidAnson/markdownlint-cli2?tab=readme-ov-file#install for installation instructions."
+ exit 1
+fi
+
+
+if [ -z "${PULL_BASE_SHA}" ]; then
+ echo "Empty base reference (\$PULL_BASE_SHA), assuming: main"
+ PULL_BASE_SHA=main
+fi
+
+if [ -z "${PULL_PULL_SHA}" ]; then
+ PULL_PULL_SHA="$(git rev-parse HEAD)"
+ echo "Empty pull reference (\$PULL_PULL_SHA), assuming: ${PULL_PULL_SHA}"
+fi
+
+MD_LINT_URL_PREFIX="https://github.com/DavidAnson/markdownlint/blob/main/doc/"
+
+mapfile -t changed_files < <(git diff "${PULL_BASE_SHA}" --name-only)
+declare -A files_with_failures start_ranges end_ranges
+
+for file in "${changed_files[@]}"; do
+ if ! [[ "$file" =~ .md$ ]]; then
+ continue
+ fi
+
+ # Find start and end ranges from changed files.
+ start_ranges=()
+ end_ranges=()
+ # From https://github.com/paleite/eslint-plugin-diff/blob/46c5bcf296e9928db19333288457bf2805aad3b9/src/git.ts#L8-L27
+ ranges=$(git diff "${PULL_BASE_SHA}" \
+ --diff-algorithm=histogram \
+ --diff-filter=ACM \
+ --find-renames=100% \
+ --no-ext-diff \
+ --relative \
+ --unified=0 -- "${file}" | \
+ gawk 'match($0, /^@@\s-[0-9,]+\s\+([0-9]+)(,([0-9]+))?/, m) { \
+ print m[1] ":" m[1] + ((m[3] == "") ? "0" : m[3]) }')
+ i=0
+ for range in ${ranges}; do
+ start_ranges["${i}"]=$(echo "${range}" | awk -F: '{print $1}')
+ end_ranges["${i}"]=$(echo "${range}" | awk -F: '{print $2}')
+ i=$((1 + i))
+ done
+ if [ -z "${ranges}" ]; then
+ start_ranges[0]=0
+ end_ranges[0]=0
+ fi
+
+ i=0
+ # Run markdownlint-cli2 with the changed file and print only the summary (stdout).
+ markdownlint-cli2 "${file}" --config "${ETCD_ROOT_DIR}/tools/.markdownlint.jsonc" 2>/dev/null || true
+ while IFS= read -r line; do
+ line_number=$(echo "${line}" | awk -F: '{print $2}' | awk '{print $1}')
+
+ while [ "${i}" -lt "${#end_ranges[@]}" ] && [ "${line_number}" -gt "${end_ranges["${i}"]}" ]; do
+ i=$((1 + i))
+ done
+ rule=$(echo "${line}" | gawk 'match($2, /([^\/]+)/, m) {print tolower(m[1])}')
+ lint_error="${line} (${MD_LINT_URL_PREFIX}${rule}.md)"
+
+ if [ "${i}" -lt "${#start_ranges[@]}" ] && [ "${line_number}" -ge "${start_ranges["${i}"]}" ] && [ "${line_number}" -le "${end_ranges["${i}"]}" ]; then
+ # Inside range with changes, raise an error.
+ log_error "${lint_error}"
+ files_with_failures["${file}"]=1
+ else
+ # Outside of range, raise a warning.
+ log_warning "${lint_error}"
+ fi
+ done < <(markdownlint-cli2 "${file}" --config "${ETCD_ROOT_DIR}/tools/.markdownlint.jsonc" 2>&1 >/dev/null || true)
+done
+
+echo "Finished linting"
+
+for file in "${!files_with_failures[@]}"; do
+ log_error "${file} has linting issues"
+done
+if [ "${#files_with_failures[@]}" -gt "0" ]; then
+ exit 1
+fi
+
diff --git a/scripts/measure-testgrid-flakiness.sh b/scripts/measure-testgrid-flakiness.sh
index 6cde54187271..2f2a6f01cf12 100755
--- a/scripts/measure-testgrid-flakiness.sh
+++ b/scripts/measure-testgrid-flakiness.sh
@@ -1,4 +1,18 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
# Measures test flakiness and create issues for flaky tests
set -euo pipefail
diff --git a/scripts/release.sh b/scripts/release.sh
index 22f2fe0e178c..7436545ea4ac 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -o errexit
set -o nounset
diff --git a/scripts/release_mod.sh b/scripts/release_mod.sh
index 70becdfd71d5..f46c224cebc1 100755
--- a/scripts/release_mod.sh
+++ b/scripts/release_mod.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
# Examples:
@@ -24,19 +37,25 @@ function _cmd() {
log_info " - push_mod_tags - Tags HEAD with all modules versions tags and pushes it to \${REMOTE_REPO}."
}
-# update_module_version [v3version]
+# update_module_version [v2version] [v3version]
# Updates versions of cross-references in all internal references in current module.
function update_module_version() {
local v3version="${1}"
+ local v2version="${2}"
local modules
run go mod tidy
- modules=$(run go list -f '{{if not .Main}}{{if not .Indirect}}{{.Path}}{{end}}{{end}}' -m all)
+ modules=$(go mod edit -json | jq -r '.Require[] | select(.Indirect | not) | .Path')
v3deps=$(echo "${modules}" | grep -E "${ROOT_MODULE}/.*/v3")
for dep in ${v3deps}; do
run go mod edit -require "${dep}@${v3version}"
done
+ v2deps=$(echo "${modules}" | grep -E "${ROOT_MODULE}/.*/v2")
+ for dep in ${v2deps}; do
+ run go mod edit -require "${dep}@${v2version}"
+ done
+
run go mod tidy
}
@@ -55,14 +74,19 @@ function update_versions_cmd() {
fi
local v3version="${TARGET_VERSION}"
+ local v2version
+ # converts e.g. v3.5.0-alpha.0 --> v2.305.0-alpha.0
+ # shellcheck disable=SC2001
+ v2version="$(echo "${TARGET_VERSION}" | sed 's|^v3.\([0-9]*\).|v2.30\1.|g')"
log_info "DRY_RUN : ${DRY_RUN}"
log_info "TARGET_VERSION: ${TARGET_VERSION}"
log_info ""
log_info "v3version: ${v3version}"
+ log_info "v2version: ${v2version}"
- run_for_modules update_module_version "${v3version}"
- run_for_modules mod_tidy_fix || exit 2
+ run_for_workspace_modules update_module_version "${v3version}" "${v2version}"
+ run_for_workspace_modules mod_tidy_fix || exit 2
}
function get_gpg_key {
@@ -86,16 +110,16 @@ function push_mod_tags_cmd {
# Any module ccan be used for this
local main_version
- main_version=$(go list -f '{{.Version}}' -m "${ROOT_MODULE}/api/v3")
+ main_version=$(go mod edit -json | jq -r '.Require[] | select(.Path == "'"${ROOT_MODULE}"'/api/v3") | .Version')
local tags=()
keyid=$(get_gpg_key) || return 2
for module in $(modules); do
local version
- version=$(go list -f '{{.Version}}' -m "${module}")
+ version=$(go mod edit -json | jq -r '.Require[] | select(.Path == "'"${module}"'") | .Version')
local path
- path=$(go list -f '{{.Path}}' -m "${module}")
+ path=$(go mod edit -json | jq -r '.Require[] | select(.Path == "'"${module}"'") | .Path')
local subdir="${path//${ROOT_MODULE}\//}"
local tag
if [ -z "${version}" ]; then
@@ -110,7 +134,7 @@ function push_mod_tags_cmd {
# consider main-module's tag as the latest.
run sleep 2
run git tag --local-user "${keyid}" --sign "${tag}" --message "${version}"
- tags=("${tags[@]}" "${tag}")
+ tags+=("${tag}")
done
maybe_run git push -f "${REMOTE_REPO}" "${tags[@]}"
}
diff --git a/scripts/sync_go_toolchain_directive.sh b/scripts/sync_go_toolchain_directive.sh
deleted file mode 100755
index 643138e1d0a9..000000000000
--- a/scripts/sync_go_toolchain_directive.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-# This script looks at the version present in the .go-version file and treats
-# that to be the value of the toolchain directive that go should use. It then
-# updates the toolchain directives of all go.mod files to reflect this version.
-#
-# We do this to ensure that .go-version acts as the source of truth for go versions.
-
-set -euo pipefail
-
-source ./scripts/test_lib.sh
-
-TARGET_GO_VERSION="${TARGET_GO_VERSION:-"$(cat "${ETCD_ROOT_DIR}/.go-version")"}"
-find . -name 'go.mod' -exec go mod edit -toolchain=go"${TARGET_GO_VERSION}" {} \;
diff --git a/scripts/test.sh b/scripts/test.sh
index bedeabb9de5a..d1207bc3f27e 100755
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
#
# Run all etcd tests
# ./scripts/test.sh
@@ -50,6 +63,9 @@ export ETCD_VERIFY=all
source ./scripts/test_lib.sh
source ./scripts/build_lib.sh
+GOPATH_BIN=$(go env GOPATH)/bin
+export PATH="${GOPATH_BIN}:${PATH}"
+
OUTPUT_FILE=${OUTPUT_FILE:-""}
if [ -n "${OUTPUT_FILE}" ]; then
@@ -57,7 +73,7 @@ if [ -n "${OUTPUT_FILE}" ]; then
exec > >(tee -a "${OUTPUT_FILE}") 2>&1
fi
-PASSES=${PASSES:-"gofmt bom dep build unit"}
+PASSES=${PASSES:-"bom dep build unit"}
KEEP_GOING_SUITE=${KEEP_GOING_SUITE:-false}
PKG=${PKG:-}
SHELLCHECK_VERSION=${SHELLCHECK_VERSION:-"v0.10.0"}
@@ -67,6 +83,18 @@ if [ -z "${GOARCH:-}" ]; then
GOARCH=$(go env GOARCH);
fi
+if [ -z "${OS:-}" ]; then
+ OS=$(uname -s | tr '[:upper:]' '[:lower:]')
+fi
+
+if [ -z "${ARCH:-}" ]; then
+ ARCH=$(uname -m)
+
+ if [ "$ARCH" = "arm64" ]; then
+ ARCH="aarch64"
+ fi
+fi
+
# determine whether target supports race detection
if [ -z "${RACE:-}" ] ; then
if [ "$GOARCH" == "amd64" ] || [ "$GOARCH" == "arm64" ]; then
@@ -93,58 +121,80 @@ fi
function build_pass {
log_callout "Building etcd"
- run_for_modules run go build "${@}" || return 2
+ run_for_workspace_modules run go build "${@}" ./... || return 2
GO_BUILD_FLAGS="-v" etcd_build "${@}"
GO_BUILD_FLAGS="-v" tools_build "${@}"
}
################# REGULAR TESTS ################################################
-# run_unit_tests [pkgs] runs unit tests for a current module and givesn set of [pkgs]
-function run_unit_tests {
- local pkgs="${1:-./...}"
- shift 1
- # shellcheck disable=SC2068 #For context see - https://github.com/etcd-io/etcd/pull/16433#issuecomment-1684312755
- GOLANG_TEST_SHORT=true go_test "${pkgs}" "parallel" : -short -timeout="${TIMEOUT:-3m}" ${COMMON_TEST_FLAGS[@]:-} ${RUN_ARG[@]:-} "$@"
-}
-
function unit_pass {
- run_for_modules run_unit_tests "$@"
+ run_for_all_workspace_modules \
+ run_go_tests -short \
+ -failfast \
+ -timeout="${TIMEOUT:-3m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@"
}
function integration_extra {
if [ -z "${PKG}" ] ; then
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./integration/v2store/..." "keep_going" : -timeout="${TIMEOUT:-5m}" ${COMMON_TEST_FLAGS[@]:-} ${RUN_ARG[@]:-} "$@" || return $?
+ run_go_tests_expanding_packages ./tests/integration/v2store/... \
+ -timeout="${TIMEOUT:-5m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@"
else
log_warning "integration_extra ignored when PKG is specified"
fi
}
function integration_pass {
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./integration/..." "parallel" : -timeout="${TIMEOUT:-15m}" ${COMMON_TEST_FLAGS[@]:-} ${RUN_ARG[@]:-} -p=2 "$@" || return $?
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./common/..." "parallel" : --tags=integration -timeout="${TIMEOUT:-15m}" ${COMMON_TEST_FLAGS[@]:-} ${RUN_ARG[@]:-} -p=2 "$@" || return $?
+ run_go_tests ./tests/integration/... \
+ -p=2 \
+ -failfast \
+ -timeout="${TIMEOUT:-15m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@" || return 2
+
+ run_go_tests ./tests/common/... \
+ -p=2 \
+ -failfast \
+ -tags=integration \
+ -timeout="${TIMEOUT:-15m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@" || return 2
+
integration_extra "$@"
}
function e2e_pass {
- # e2e tests are running pre-build binary. Settings like --race,-cover,-cpu does not have any impact.
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./e2e/..." "keep_going" : -timeout="${TIMEOUT:-30m}" ${RUN_ARG[@]:-} "$@" || return $?
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./common/..." "keep_going" : --tags=e2e -timeout="${TIMEOUT:-30m}" ${RUN_ARG[@]:-} "$@"
+ # e2e tests are running pre-build binary. Settings like --race,-cover,-cpu do not have any impact.
+ run_go_tests_expanding_packages ./tests/e2e/... \
+ -timeout="${TIMEOUT:-30m}" \
+ "${RUN_ARG[@]}" \
+ "$@" || return 2
+
+ run_go_tests_expanding_packages ./tests/common/... \
+ -tags=e2e \
+ -timeout="${TIMEOUT:-30m}" \
+ "${RUN_ARG[@]}" \
+ "$@"
}
function robustness_pass {
# e2e tests are running pre-build binary. Settings like --race,-cover,-cpu does not have any impact.
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./robustness" "keep_going" : -timeout="${TIMEOUT:-30m}" ${RUN_ARG[@]:-} "$@"
+ run_go_tests ./tests/robustness \
+ -timeout="${TIMEOUT:-30m}" \
+ "${RUN_ARG[@]}" \
+ "$@"
}
function integration_e2e_pass {
- run_pass "integration" "${@}"
+ run_pass "integration" "${@}" || return 2
run_pass "e2e" "${@}"
}
@@ -166,46 +216,30 @@ function generic_checker {
}
function grpcproxy_pass {
- run_pass "grpcproxy_integration" "${@}"
+ run_pass "grpcproxy_integration" "${@}" || return 2
run_pass "grpcproxy_e2e" "${@}"
}
function grpcproxy_integration_pass {
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./integration/..." "fail_fast" : -timeout=30m -tags cluster_proxy ${COMMON_TEST_FLAGS[@]:-} "$@"
+ run_go_tests_expanding_packages ./tests/integration/... \
+ -tags=cluster_proxy \
+ -timeout="${TIMEOUT:-30m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@"
}
function grpcproxy_e2e_pass {
- # shellcheck disable=SC2068
- run_for_module "tests" go_test "./e2e" "fail_fast" : -timeout=30m -tags cluster_proxy ${COMMON_TEST_FLAGS[@]:-} "$@"
+ run_go_tests_expanding_packages ./tests/e2e/... \
+ -tags=cluster_proxy \
+ -timeout="${TIMEOUT:-30m}" \
+ "${COMMON_TEST_FLAGS[@]}" \
+ "${RUN_ARG[@]}" \
+ "$@"
}
################# COVERAGE #####################################################
-# pkg_to_coverflag [prefix] [pkgs]
-# produces name of .coverprofile file to be used for tests of this package
-function pkg_to_coverprofileflag {
- local prefix="${1}"
- local pkgs="${2}"
- local pkgs_normalized
- prefix_normalized=$(echo "${prefix}" | tr "./ " "__+")
- if [ "${pkgs}" == "./..." ]; then
- pkgs_normalized="all"
- else
- pkgs_normalized=$(echo "${pkgs}" | tr "./ " "__+")
- fi
- mkdir -p "${coverdir}/${prefix_normalized}"
- echo -n "-coverprofile=${coverdir}/${prefix_normalized}/${pkgs_normalized}.coverprofile"
-}
-
-function not_test_packages {
- for m in $(modules); do
- if [[ $m =~ .*/etcd/tests/v3 ]]; then continue; fi
- if [[ $m =~ .*/etcd/v3 ]]; then continue; fi
- echo "${m}/..."
- done
-}
-
# split_dir [dir] [num]
function split_dir {
local d="${1}"
@@ -249,18 +283,6 @@ function merge_cov_files {
done
}
-# merge_cov [coverdir]
-function merge_cov {
- log_callout "[$(date)] Merging coverage files ..."
- coverdir="${1}"
- for d in "${coverdir}"/*/; do
- d=${d%*/} # remove the trailing "/"
- merge_cov_files "${d}" "${d}.coverprofile" &
- done
- wait
- merge_cov_files "${coverdir}" "${coverdir}/all.coverprofile"
-}
-
# https://docs.codecov.com/docs/unexpected-coverage-changes#reasons-for-indirect-changes
function cov_pass {
# shellcheck disable=SC2153
@@ -272,49 +294,45 @@ function cov_pass {
local coverdir
coverdir=$(readlink -f "${COVERDIR}")
mkdir -p "${coverdir}"
- find "${coverdir}" -print0 -name '*.coverprofile' | xargs -0 rm
+ find "${coverdir}" -name '*.coverprofile' -delete
+
+ local modules=()
+ load_workspace_relative_modules modules
+ local covpkgs=()
+ for module in "${modules[@]}"; do
+ if [[ ! "${module}" =~ ^./tests && ! "${module}" =~ ^./...$ ]]; then
+ covpkgs+=("${module}")
+ fi
+ done
- local covpkgs
- covpkgs=$(not_test_packages)
local coverpkg_comma
coverpkg_comma=$(echo "${covpkgs[@]}" | xargs | tr ' ' ',')
local gocov_build_flags=("-covermode=set" "-coverpkg=$coverpkg_comma")
- local failed=""
+ local failed=()
log_callout "[$(date)] Collecting coverage from unit tests ..."
- for m in $(module_dirs); do
- GOLANG_TEST_SHORT=true run_for_module "${m}" go_test "./..." "parallel" "pkg_to_coverprofileflag unit_${m}" -short -timeout=30m \
- "${gocov_build_flags[@]}" "$@" || failed="$failed unit"
- done
+ run_go_tests "${modules[@]}" -short -timeout=30m "${gocov_build_flags[@]}" -coverprofile="${coverdir}/unit.coverprofile" "$@" || failed+=("unit")
log_callout "[$(date)] Collecting coverage from integration tests ..."
- run_for_module "tests" go_test "./integration/..." "parallel" "pkg_to_coverprofileflag integration" \
- -timeout=30m "${gocov_build_flags[@]}" "$@" || failed="$failed integration"
+ run_go_tests "./tests/integration/..." -timeout=30m "${gocov_build_flags[@]}" -coverprofile="${coverdir}/integration.coverprofile" "$@" || failed+=("integration")
# integration-store-v2
- run_for_module "tests" go_test "./integration/v2store/..." "keep_going" "pkg_to_coverprofileflag store_v2" \
- -timeout=5m "${gocov_build_flags[@]}" "$@" || failed="$failed integration_v2"
+ run_go_tests "./tests/integration/v2store/..." -timeout=5m "${gocov_build_flags[@]}" -coverprofile="${coverdir}/integration_v2.coverprofile" "$@" || failed+=("integration_v2")
# integration_cluster_proxy
- run_for_module "tests" go_test "./integration/..." "parallel" "pkg_to_coverprofileflag integration_cluster_proxy" \
- -tags cluster_proxy -timeout=30m "${gocov_build_flags[@]}" || failed="$failed integration_cluster_proxy"
+ run_go_tests "./tests/integration/..." -tags cluster_proxy -timeout=30m "${gocov_build_flags[@]}" -coverprofile="${coverdir}/integration_cluster_proxy.coverprofile" "$@" || failed+=("integration_cluster_proxy")
+ run_go_tests "./tests/common/" -tags integration -timeout=30m "${gocov_build_flags[@]}" -coverprofile="${coverdir}/common.coverprofile" "$@" || failed+=("common")
local cover_out_file="${coverdir}/all.coverprofile"
- merge_cov "${coverdir}"
+ merge_cov_files "${coverdir}" "${cover_out_file}"
# strip out generated files (using GNU-style sed)
sed --in-place -E "/[.]pb[.](gw[.])?go/d" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/api/v3/|api/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/client/v3/|client/v3/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/client/pkg/v3|client/pkg/v3/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/etcdctl/v3/|etcdctl/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/etcdutl/v3/|etcdutl/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/pkg/v3/|pkg/|g" "${cover_out_file}" || true
- sed --in-place -E "s|go.etcd.io/etcd/server/v3/|server/|g" "${cover_out_file}" || true
+ sed --in-place -E "s|go.etcd.io/etcd/(.*)/v3|\1|g" "${cover_out_file}" || true
# held failures to generate the full coverage file, now fail
- if [ -n "$failed" ]; then
- for f in $failed; do
+ if [ "${#failed[@]}" -gt 0 ]; then
+ for f in "${failed[@]}"; do
log_error "--- FAIL:" "$f"
done
log_warning "Despite failures, you can see partial report:"
@@ -331,7 +349,7 @@ function shellcheck_pass {
SHELLCHECK=shellcheck
if ! tool_exists "shellcheck" "https://github.com/koalaman/shellcheck#installing"; then
log_callout "Installing shellcheck $SHELLCHECK_VERSION"
- wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar -xJv -C /tmp/ --strip-components=1
+ wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.${OS}.${ARCH}.tar.xz" | tar -xJv -C /tmp/ --strip-components=1
mkdir -p ./bin
mv /tmp/shellcheck ./bin/
SHELLCHECK=./bin/shellcheck
@@ -340,10 +358,10 @@ function shellcheck_pass {
}
function shellws_pass {
- log_callout "Ensuring no tab-based indention in shell scripts"
+ log_callout "Ensuring no tab-based indentation in shell scripts"
local files
if files=$(find . -name '*.sh' -print0 | xargs -0 grep -E -n $'^\s*\t'); then
- log_error "FAIL: found tab-based indention in the following bash scripts. Use ' ' (double space):"
+ log_error "FAIL: found tab-based indentation in the following bash scripts. Use ' ' (double space):"
log_error "${files}"
log_warning "Suggestion: run \"make fix\" to address the issue."
return 255
@@ -356,7 +374,14 @@ function markdown_marker_pass {
# TODO: check other markdown files when marker handles headers with '[]'
if ! tool_exists "$marker" "https://crates.io/crates/marker"; then
log_callout "Installing markdown marker $MARKDOWN_MARKER_VERSION"
- wget -qO- "https://github.com/crawford/marker/releases/download/${MARKDOWN_MARKER_VERSION}/marker-${MARKDOWN_MARKER_VERSION}-x86_64-unknown-linux-musl.tar.gz" | tar -xzv -C /tmp/ --strip-components=1 >/dev/null
+ MARKER_OS=$OS
+ if [ "$OS" = "darwin" ]; then
+ MARKER_OS="apple-darwin"
+ elif [ "$OS" = "linux" ]; then
+ MARKER_OS="unknown-linux-musl"
+ fi
+
+ wget -qO- "https://github.com/crawford/marker/releases/download/${MARKDOWN_MARKER_VERSION}/marker-${MARKDOWN_MARKER_VERSION}-${ARCH}-${MARKER_OS}.tar.gz" | tar -xzv -C /tmp/ --strip-components=1 >/dev/null
mkdir -p ./bin
mv /tmp/marker ./bin/
marker=./bin/marker
@@ -366,127 +391,39 @@ function markdown_marker_pass {
}
function govuln_pass {
- run_for_modules run govulncheck -show verbose
-}
-
-function govet_pass {
- run_for_modules generic_checker run go vet
-}
-
-function govet_shadow_per_package {
- local shadow
- shadow=$1
-
- # skip grpc_gateway packages because
- #
- # stderr: etcdserverpb/gw/rpc.pb.gw.go:2100:3: declaration of "ctx" shadows declaration at line 2005
- local skip_pkgs=(
- "go.etcd.io/etcd/api/v3/etcdserverpb/gw"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v3lock/v3lockpb/gw"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v3election/v3electionpb/gw"
- )
-
- local pkgs=()
- while IFS= read -r line; do
- local in_skip_pkgs="false"
-
- for pkg in "${skip_pkgs[@]}"; do
- if [ "${pkg}" == "${line}" ]; then
- in_skip_pkgs="true"
- break
- fi
- done
-
- if [ "${in_skip_pkgs}" == "true" ]; then
- continue
- fi
-
- pkgs+=("${line}")
- done < <(go list ./...)
-
- run go vet -all -vettool="${shadow}" "${pkgs[@]}"
-}
-
-function govet_shadow_pass {
- local shadow
- shadow=$(tool_get_bin "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow")
-
- run_for_modules generic_checker govet_shadow_per_package "${shadow}"
+ run go install golang.org/x/vuln/cmd/govulncheck@latest
+ run_for_all_workspace_modules run govulncheck -show verbose
}
function lint_pass {
- run_for_modules generic_checker run golangci-lint run --config "${ETCD_ROOT_DIR}/tools/.golangci.yaml" --show-stats=false
+ run_for_all_workspace_modules golangci-lint run --config "${ETCD_ROOT_DIR}/tools/.golangci.yaml"
}
function lint_fix_pass {
- run_for_modules generic_checker run golangci-lint run --config "${ETCD_ROOT_DIR}/tools/.golangci.yaml" --fix --show-stats=false
-}
-
-function license_header_per_module {
- # bash 3.x compatible replacement of: mapfile -t gofiles < <(go_srcs_in_module)
- local gofiles=()
- while IFS= read -r line; do gofiles+=("$line"); done < <(go_srcs_in_module)
- run_go_tool "github.com/google/addlicense" --check "${gofiles[@]}"
-}
-
-function license_header_pass {
- run_for_modules generic_checker license_header_per_module
-}
-
-# goword_for_package package
-# checks spelling and comments in the 'package' in the current module
-#
-function goword_for_package {
- # bash 3.x compatible replacement of: mapfile -t gofiles < <(go_srcs_in_module)
- local gofiles=()
- while IFS= read -r line; do gofiles+=("$line"); done < <(go_srcs_in_module)
-
- local gowordRes
-
- # spellchecking can be enabled with GOBINARGS="--tags=spell"
- # but it requires heavy dependencies installation, like:
- # apt-get install libaspell-dev libhunspell-dev hunspell-en-us aspell-en
-
- # only check for broke exported godocs
- if gowordRes=$(run_go_tool "github.com/chzchzchz/goword" -use-spell=false "${gofiles[@]}" | grep godoc-export | sort); then
- log_error -e "goword checking failed:\\n${gowordRes}"
- return 255
- fi
- if [ -n "$gowordRes" ]; then
- log_error -e "goword checking returned output:\\n${gowordRes}"
- return 255
- fi
-}
-
-
-function goword_pass {
- run_for_modules goword_for_package || return 255
-}
-
-function go_fmt_for_package {
- # We utilize 'go fmt' to find all files suitable for formatting,
- # but reuse full power gofmt to perform just RO check.
- go fmt -n "$1" | sed 's| -w | -d |g' | sh
-}
-
-function gofmt_pass {
- run_for_modules generic_checker go_fmt_for_package
+ run_for_all_workspace_modules golangci-lint run --config "${ETCD_ROOT_DIR}/tools/.golangci.yaml" --fix
}
function bom_pass {
log_callout "Checking bill of materials..."
- # https://github.com/golang/go/commit/7c388cc89c76bc7167287fb488afcaf5a4aa12bf
- # shellcheck disable=SC2207
- modules=($(modules_for_bom))
+ local _bom_modules=()
+ load_workspace_relative_modules_for_bom _bom_modules
# Internally license-bill-of-materials tends to modify go.sum
run cp go.sum go.sum.tmp || return 2
run cp go.mod go.mod.tmp || return 2
- output=$(GOFLAGS=-mod=mod run_go_tool github.com/appscodelabs/license-bill-of-materials \
+ # Intentionally run the command once first, so it fetches dependencies. The exit code on the first
+ # run in a just cloned repository is always dirty.
+ GOOS=linux run_go_tool github.com/appscodelabs/license-bill-of-materials \
+ --override-file ./bill-of-materials.override.json "${_bom_modules[@]}" &>/dev/null
+
+ # BOM file should be generated for linux. Otherwise running this command on other operating systems such as OSX
+ # results in certain dependencies being excluded from the BOM file, such as procfs.
+ # For more info, https://github.com/etcd-io/etcd/issues/19665
+ output=$(GOOS=linux run_go_tool github.com/appscodelabs/license-bill-of-materials \
--override-file ./bill-of-materials.override.json \
- "${modules[@]}")
- code="$?"
+ "${_bom_modules[@]}")
+ local code="$?"
run cp go.sum.tmp go.sum || return 2
run cp go.mod.tmp go.mod || return 2
@@ -504,41 +441,61 @@ function bom_pass {
rm bom-now.json.tmp
}
+function module_gomodguard {
+ if [ ! -f .gomodguard.yaml ]; then
+ # Nothing to validate, return.
+ return
+ fi
+
+ local tool_bin="$1"
+ run "${tool_bin}"
+}
+
+function gomodguard_pass {
+ local tool_bin
+ tool_bin=$(tool_get_bin github.com/ryancurrah/gomodguard/cmd/gomodguard)
+ run_for_workspace_modules module_gomodguard "${tool_bin}"
+}
+
######## VARIOUS CHECKERS ######################################################
-function dump_deps_of_module() {
+function dump_module_deps() {
+ local json_mod
+ json_mod=$(run go mod edit -json)
+
local module
- if ! module=$(run go list -m); then
+ if ! module=$(echo "${json_mod}" | jq -r .Module.Path); then
return 255
fi
- run go mod edit -json | jq -r '.Require[] | .Path+","+.Version+","+if .Indirect then " (indirect)" else "" end+",'"${module}"'"'
+
+ local require
+ require=$(echo "${json_mod}" | jq -r '.Require')
+ if [ "$require" == "null" ]; then
+ return 0
+ fi
+
+ echo "$require" | jq -r '.[] | .Path+","+.Version+","+if .Indirect then " (indirect)" else "" end+",'"${module}"'"'
}
# Checks whether dependencies are consistent across modules
function dep_pass {
local all_dependencies
- local tools_mod_dependencies
- all_dependencies=$(run_for_modules dump_deps_of_module | sort) || return 2
- # tools/mod is a special case. It is a module that is not included in the
- # module list from test_lib.sh. However, we need to ensure that the
- # dependency versions match the rest of the project. Therefore, explicitly
- # execute the command for tools/mod, and append its dependencies to the list.
- tools_mod_dependencies=$(run_for_module "tools/mod" dump_deps_of_module "./...") || return 2
- all_dependencies="${all_dependencies}"$'\n'"${tools_mod_dependencies}"
+ all_dependencies=$(run_for_workspace_modules dump_module_deps | sort) || return 2
local duplicates
duplicates=$(echo "${all_dependencies}" | cut -d ',' -f 1,2 | sort | uniq | cut -d ',' -f 1 | sort | uniq -d) || return 2
- for dup in ${duplicates}; do
- log_error "FAIL: inconsistent versions for dependency: ${dup}"
- echo "${all_dependencies}" | grep "${dup}," | sed 's|\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)| - \1@\2\3 from: \4|g'
- done
if [[ -n "${duplicates}" ]]; then
+ for dup in ${duplicates}; do
+ log_error "FAIL: inconsistent versions for dependency: ${dup}"
+ echo "${all_dependencies}" | grep "${dup}," | sed 's|\([^,]*\),\([^,]*\),\([^,]*\),\([^,]*\)| - \1@\2\3 from: \4|g'
+ done
+
log_error "FAIL: inconsistent dependencies"
return 2
- else
- log_success "SUCCESS: dependencies are consistent across modules"
fi
+
+ log_success "SUCCESS: dependencies are consistent across modules"
}
function release_pass {
@@ -565,7 +522,7 @@ function release_pass {
UPGRADE_VER=$(git ls-remote --tags https://github.com/etcd-io/etcd.git \
| grep --only-matching --perl-regexp "(?<=v)${binary_major}.${previous_minor}.[\d]+?(?=[\^])" \
| sort --numeric-sort --key 1.5 | tail -1 | sed 's/^/v/')
- log_callout "Found latest release: ${UPGRADE_VER}."
+ log_callout "Found previous minor version (v${binary_major}.${previous_minor}) latest release: ${UPGRADE_VER}."
if [ -n "${MANUAL_VER:-}" ]; then
# in case, we need to test against different version
@@ -629,12 +586,8 @@ EOF
VERSION="${VERSION}" run "${ETCD_ROOT_DIR}/scripts/test_images.sh"
}
-function mod_tidy_for_module {
- run go mod tidy -diff
-}
-
function mod_tidy_pass {
- run_for_modules generic_checker mod_tidy_for_module
+ run_for_workspace_modules run go mod tidy -diff
}
function proto_annotations_pass {
@@ -645,6 +598,17 @@ function genproto_pass {
"${ETCD_ROOT_DIR}/scripts/verify_genproto.sh"
}
+function go_workspace_pass {
+ log_callout "Ensuring go workspace is in sync."
+
+ run go mod download
+ if [ -n "$(git status --porcelain go.work.sum)" ]; then
+ log_error "Go workspace not in sync."
+ log_warning "Suggestion: run \"make fix\" to address the issue."
+ return 255
+ fi
+}
+
########### MAIN ###############################################################
function run_pass {
diff --git a/scripts/test_images.sh b/scripts/test_images.sh
index f4f18a6940a7..11138824bdb0 100755
--- a/scripts/test_images.sh
+++ b/scripts/test_images.sh
@@ -1,4 +1,17 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
diff --git a/scripts/test_lib.sh b/scripts/test_lib.sh
index e3fe2de59a62..c5234e45b924 100644
--- a/scripts/test_lib.sh
+++ b/scripts/test_lib.sh
@@ -1,7 +1,22 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
set -euo pipefail
+source ./scripts/test_utils.sh
+
ROOT_MODULE="go.etcd.io/etcd"
if [[ "$(go list)" != "${ROOT_MODULE}/v3" ]]; then
@@ -15,104 +30,8 @@ function set_root_dir {
set_root_dir
-#### Convenient IO methods #####
-
-COLOR_RED='\033[0;31m'
-COLOR_ORANGE='\033[0;33m'
-COLOR_GREEN='\033[0;32m'
-COLOR_LIGHTCYAN='\033[0;36m'
-COLOR_BLUE='\033[0;94m'
-COLOR_MAGENTA='\033[95m'
-COLOR_BOLD='\033[1m'
-COLOR_NONE='\033[0m' # No Color
-
-
-function log_error {
- >&2 echo -n -e "${COLOR_BOLD}${COLOR_RED}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-function log_warning {
- >&2 echo -n -e "${COLOR_ORANGE}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-function log_callout {
- >&2 echo -n -e "${COLOR_LIGHTCYAN}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-function log_cmd {
- >&2 echo -n -e "${COLOR_BLUE}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-function log_success {
- >&2 echo -n -e "${COLOR_GREEN}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-function log_info {
- >&2 echo -n -e "${COLOR_NONE}"
- >&2 echo "$@"
- >&2 echo -n -e "${COLOR_NONE}"
-}
-
-# From http://stackoverflow.com/a/12498485
-function relativePath {
- # both $1 and $2 are absolute paths beginning with /
- # returns relative path to $2 from $1
- local source=$1
- local target=$2
-
- local commonPart=$source
- local result=""
-
- while [[ "${target#"$commonPart"}" == "${target}" ]]; do
- # no match, means that candidate common part is not correct
- # go up one level (reduce common part)
- commonPart="$(dirname "$commonPart")"
- # and record that we went back, with correct / handling
- if [[ -z $result ]]; then
- result=".."
- else
- result="../$result"
- fi
- done
-
- if [[ $commonPart == "/" ]]; then
- # special case for root (no common path)
- result="$result/"
- fi
-
- # since we now have identified the common part,
- # compute the non-common part
- local forwardPart="${target#"$commonPart"}"
-
- # and now stick all parts together
- if [[ -n $result ]] && [[ -n $forwardPart ]]; then
- result="$result$forwardPart"
- elif [[ -n $forwardPart ]]; then
- # extra slash removal
- result="${forwardPart:1}"
- fi
-
- echo "$result"
-}
-
#### Discovery of files/packages within a go module #####
-# go_srcs_in_module
-# returns list of all not-generated go sources in the current (dir) module.
-function go_srcs_in_module {
- go list -f "{{with \$c:=.}}{{range \$f:=\$c.GoFiles }}{{\$c.Dir}}/{{\$f}}{{\"\n\"}}{{end}}{{range \$f:=\$c.TestGoFiles }}{{\$c.Dir}}/{{\$f}}{{\"\n\"}}{{end}}{{range \$f:=\$c.XTestGoFiles }}{{\$c.Dir}}/{{\$f}}{{\"\n\"}}{{end}}{{end}}" ./... | grep -vE "(\\.pb\\.go|\\.pb\\.gw.go)"
-}
-
# pkgs_in_module [optional:package_pattern]
# returns list of all packages in the current (dir) module.
# if the package_pattern is given, its being resolved.
@@ -166,7 +85,7 @@ function run_for_module {
}
function module_dirs() {
- echo "api pkg client/pkg client/v3 server etcdutl etcdctl tests tools/mod tools/rw-heatmaps tools/testgrid-analysis ."
+ echo "api pkg client/pkg client/v3 server etcdutl etcdctl tests tools/mod tools/rw-heatmaps tools/testgrid-analysis cache ."
}
# maybe_run [cmd...] runs given command depending on the DRY_RUN flag.
@@ -195,12 +114,70 @@ function modules() {
echo "${modules[@]}"
}
-function modules_for_bom() {
- for m in $(modules); do
- echo -n "${m}/... "
+# Receives a reference to an array variable, and returns the workspace relative modules.
+function load_workspace_relative_modules() {
+ local -n _relative_modules=$1
+ while IFS= read -r line; do _relative_modules+=("$line"); done < <(
+ go work edit -json | jq -r '.Use[].DiskPath + "/..."'
+ )
+}
+
+# Receives a reference to an array variable, and returns the workspace relative modules, not
+# including the tools, as they are not considered to be added to the bill for materials.
+function load_workspace_relative_modules_for_bom() {
+ local -n relative_modules_for_bom=$1
+ local modules=()
+ load_workspace_relative_modules modules
+ for module in "${modules[@]}"; do
+ if [[ ! "${module}" =~ ^./tools ]]; then
+ relative_modules_for_bom+=("${module}")
+ fi
done
}
+# run_for_all_workspace_modules [cmd]
+# run given command across all workspace modules
+# (unless the set is limited using ${PKG} or / ${USERMOD})
+function run_for_all_workspace_modules {
+ local pkg="${PKG:-./...}"
+ if [ -z "${USERMOD:-}" ]; then
+ local _modules=()
+ load_workspace_relative_modules _modules
+ run "$@" "${_modules[@]}"
+ else
+ run_for_module "${USERMOD}" "$@" "${pkg}" || return "$?"
+ fi
+}
+
+# run_for_workspace_modules [cmd]
+# run given command in each individual workspace module
+# (unless the set is limited using ${PKG} or / ${USERMOD})
+function run_for_workspace_modules {
+ local keep_going_module=${KEEP_GOING_MODULE:-false}
+ local fail_mod=false
+ local pkg="${PKG:-./...}"
+
+ if [ -z "${USERMOD:-}" ]; then
+ local _modules=()
+ load_workspace_relative_modules _modules
+ for module in "${_modules[@]}"; do
+ if ! run_for_module "${module%...}" "$@"; then
+ if [ "$keep_going_module" = false ]; then
+ log_error "There was a Failure in module ${module}, aborting..."
+ return 1
+ fi
+ log_error "There was a Failure in module ${module}, keep going..."
+ fail_mod=true
+ fi
+ done
+ if [ "$fail_mod" = true ]; then
+ return 1
+ fi
+ else
+ run_for_module "${USERMOD}" "$@" "${pkg}" || return "$?"
+ fi
+}
+
# run_for_modules [cmd]
# run given command across all modules and packages
# (unless the set is limited using ${PKG} or / ${USERMOD})
@@ -229,14 +206,15 @@ function run_for_modules {
fi
}
-junitFilenamePrefix() {
- if [[ -z "${JUNIT_REPORT_DIR:-}" ]]; then
+function get_junit_filename_prefix {
+ local junit_report_dir="$1"
+ if [[ -z "${junit_report_dir}" ]]; then
echo ""
return
fi
- mkdir -p "${JUNIT_REPORT_DIR}"
- DATE=$( date +%s | base64 | head -c 15 )
- echo "${JUNIT_REPORT_DIR}/junit_$DATE"
+
+ mkdir -p "${junit_report_dir}"
+ mktemp --dry-run "${junit_report_dir}/junit_XXXXXXXXXX"
}
function produce_junit_xmlreport {
@@ -260,102 +238,66 @@ function produce_junit_xmlreport {
#### Running go test ########
-# go_test [packages] [mode] [flags_for_package_func] [$@]
-# [mode] supports 3 states:
-# - "parallel": fastest as concurrently processes multiple packages, but silent
-# till the last package. See: https://github.com/golang/go/issues/2731
-# - "keep_going" : executes tests package by package, but postpones reporting error to the last
-# - "fail_fast" : executes tests packages 1 by 1, exits on the first failure.
-#
-# [flags_for_package_func] is a name of function that takes list of packages as parameter
-# and computes additional flags to the go_test commands.
-# Use 'true' or ':' if you dont need additional arguments.
-#
-# depends on the VERBOSE top-level variable.
-#
-# Example:
-# go_test "./..." "keep_going" ":" --short
-#
-# The function returns != 0 code in case of test failure.
-function go_test {
- local packages="${1}"
- local mode="${2}"
- local flags_for_package_func="${3}"
- local junit_filename_prefix
+# run_go_tests_expanding_packages [arguments to pass to go test]
+# Expands the packages in the list of arguments, i.e. ./... into a list of
+# packages for that given module. Then, it calls run_go_tests with the expanded
+# packages. Implements the legacy modes for non-parallel testing.
+function run_go_tests_expanding_packages {
+ local packages=()
+ local args=()
+ for arg in "$@"; do
+ if [[ "${arg}" =~ ^\./ || "${arg}" =~ ^go\.etcd\.io/etcd ]]; then
+ packages+=("${arg}")
+ else
+ args+=("${arg}")
+ fi
+ done
- shift 3
+ # Expanding patterns (like ./...) into list of packages
+ local unpacked_packages=()
+ while IFS='' read -r line; do unpacked_packages+=("$line"); done < <(
+ go list "${packages[@]}"
+ )
- local goTestFlags=""
- local goTestEnv=""
+ run_go_tests "${unpacked_packages[@]}" "${args[@]}"
+}
- ##### Create a junit-style XML test report in this directory if set. #####
- JUNIT_REPORT_DIR=${JUNIT_REPORT_DIR:-}
+# run_go_test [arguments to pass to go test]
+# The following environment variables affect how the tests run:
+# - JUNIT_REPORT_DIR/ARTIFACTS: Enables collecting JUnit XML reports.
+# - VERBOSE: Sets a verbose output.
+#
+# Example:
+# KEEP_GOING_TESTS=true run_go_tests "./..." --short
+#
+# The function returns != 0 code in case of test failure.
+function run_go_tests {
+ local go_test_flags=()
# If JUNIT_REPORT_DIR is unset, and ARTIFACTS is set, then have them match.
- if [[ -z "${JUNIT_REPORT_DIR:-}" && -n "${ARTIFACTS:-}" ]]; then
- export JUNIT_REPORT_DIR="${ARTIFACTS}"
- fi
-
- # Used to filter verbose test output.
- go_test_grep_pattern=".*"
+ local junit_report_dir=${JUNIT_REPORT_DIR:-${ARTIFACTS:-}}
- if [[ -n "${JUNIT_REPORT_DIR}" ]] ; then
- goTestFlags+="-v "
- goTestFlags+="-json "
+ local go_test_grep_pattern=".*"
+ if [[ -n "${junit_report_dir}" ]]; then
# Show only summary lines by matching lines like "status package/test"
go_test_grep_pattern="^[^[:space:]]\+[[:space:]]\+[^[:space:]]\+/[^[[:space:]]\+"
fi
- junit_filename_prefix=$(junitFilenamePrefix)
-
- if [ "${VERBOSE:-}" == "1" ]; then
- goTestFlags="-v "
- goTestFlags+="-json "
- fi
-
- # Expanding patterns (like ./...) into list of packages
-
- local unpacked_packages=("${packages}")
- if [ "${mode}" != "parallel" ]; then
- # shellcheck disable=SC2207
- # shellcheck disable=SC2086
- if ! unpacked_packages=($(go list ${packages})); then
- log_error "Cannot resolve packages: ${packages}"
- return 255
- fi
- fi
-
- if [ "${mode}" == "fail_fast" ]; then
- goTestFlags+="-failfast "
+ if [[ -n "${junit_report_dir}" || "${VERBOSE:-}" == "1" ]]; then
+ go_test_flags+=("-v" "-json")
fi
- local failures=""
-
- # execution of tests against packages:
- for pkg in "${unpacked_packages[@]}"; do
- local additional_flags
- # shellcheck disable=SC2086
- additional_flags=$(${flags_for_package_func} ${pkg})
+ local cmd=(go test "${go_test_flags[@]}" "$@")
- # shellcheck disable=SC2206
- local cmd=( go test ${goTestFlags} ${additional_flags} ${pkg} "$@" )
+ local junit_filename_prefix
+ junit_filename_prefix=$(get_junit_filename_prefix "${junit_report_dir}")
- # shellcheck disable=SC2086
- if ! run env ${goTestEnv} ETCD_VERIFY="${ETCD_VERIFY}" "${cmd[@]}" | tee ${junit_filename_prefix:+"${junit_filename_prefix}.stdout"} | grep --binary-files=text "${go_test_grep_pattern}" ; then
- if [ "${mode}" != "keep_going" ]; then
- produce_junit_xmlreport "${junit_filename_prefix}"
- return 2
- else
- failures=("${failures[@]}" "${pkg}")
- fi
- fi
+ if ! run env ETCD_VERIFY="${ETCD_VERIFY}" "${cmd[@]}" | tee ${junit_filename_prefix:+"${junit_filename_prefix}.stdout"} | grep --binary-files=text "${go_test_grep_pattern}" ; then
produce_junit_xmlreport "${junit_filename_prefix}"
- done
-
- if [ -n "${failures[*]}" ] ; then
- log_error -e "ERROR: Tests for following packages failed:\\n ${failures[*]}"
return 2
fi
+
+ produce_junit_xmlreport "${junit_filename_prefix}"
}
#### Other ####
@@ -375,7 +317,8 @@ function tool_exists {
fi
}
-# tool_get_bin [tool] - returns absolute path to a tool binary (or returns error)
+# tool_get_bin [tool] - returns absolute path to a tool binary (or returns error).
+# This function is only used to run commands that are managed by tools/mod.
function tool_get_bin {
local tool="$1"
local pkg_part="$1"
@@ -407,7 +350,7 @@ function tool_pkg_dir {
# tool_get_bin [tool]
function run_go_tool {
local cmdbin
- if ! cmdbin=$(GOARCH="" tool_get_bin "${1}"); then
+ if ! cmdbin=$(GOARCH="" GOOS="" tool_get_bin "${1}"); then
log_warning "Failed to install tool '${1}'"
return 2
fi
@@ -446,7 +389,7 @@ function git_assert_branch_in_sync {
if [ -n "${branch}" ]; then
ref_local=$(run git rev-parse "${branch}")
ref_origin=$(run git rev-parse "origin/${branch}")
- if [ "x${ref_local}" != "x${ref_origin}" ]; then
+ if [ "${ref_local}" != "${ref_origin}" ]; then
log_error "In workspace '$(pwd)' the branch: ${branch} diverges from the origin."
log_error "Consider cleaning up / renaming this directory or (cd $(pwd) && git reset --hard origin/${branch})"
return 2
diff --git a/scripts/test_utils.sh b/scripts/test_utils.sh
new file mode 100644
index 000000000000..ab914aa4928e
--- /dev/null
+++ b/scripts/test_utils.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+set -euo pipefail
+
+#### Convenient IO methods #####
+
+export COLOR_RED='\033[0;31m'
+export COLOR_ORANGE='\033[0;33m'
+export COLOR_GREEN='\033[0;32m'
+export COLOR_LIGHTCYAN='\033[0;36m'
+export COLOR_BLUE='\033[0;94m'
+export COLOR_BOLD='\033[1m'
+export COLOR_MAGENTA='\033[95m'
+export COLOR_NONE='\033[0m' # No Color
+
+
+function log_error {
+ >&2 echo -n -e "${COLOR_BOLD}${COLOR_RED}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+function log_warning {
+ >&2 echo -n -e "${COLOR_ORANGE}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+function log_callout {
+ >&2 echo -n -e "${COLOR_LIGHTCYAN}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+function log_cmd {
+ >&2 echo -n -e "${COLOR_BLUE}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+function log_success {
+ >&2 echo -n -e "${COLOR_GREEN}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+function log_info {
+ >&2 echo -n -e "${COLOR_NONE}"
+ >&2 echo "$@"
+ >&2 echo -n -e "${COLOR_NONE}"
+}
+
+# From http://stackoverflow.com/a/12498485
+function relativePath {
+ # both $1 and $2 are absolute paths beginning with /
+ # returns relative path to $2 from $1
+ local source=$1
+ local target=$2
+
+ local commonPart=$source
+ local result=""
+
+ while [[ "${target#"$commonPart"}" == "${target}" ]]; do
+ # no match, means that candidate common part is not correct
+ # go up one level (reduce common part)
+ commonPart="$(dirname "$commonPart")"
+ # and record that we went back, with correct / handling
+ if [[ -z $result ]]; then
+ result=".."
+ else
+ result="../$result"
+ fi
+ done
+
+ if [[ $commonPart == "/" ]]; then
+ # special case for root (no common path)
+ result="$result/"
+ fi
+
+ # since we now have identified the common part,
+ # compute the non-common part
+ local forwardPart="${target#"$commonPart"}"
+
+ # and now stick all parts together
+ if [[ -n $result ]] && [[ -n $forwardPart ]]; then
+ result="$result$forwardPart"
+ elif [[ -n $forwardPart ]]; then
+ # extra slash removal
+ result="${forwardPart:1}"
+ fi
+
+ echo "$result"
+}
diff --git a/scripts/update_dep.sh b/scripts/update_dep.sh
index e76ecaff5c5a..c0ea4d1503ed 100755
--- a/scripts/update_dep.sh
+++ b/scripts/update_dep.sh
@@ -1,8 +1,21 @@
-#!/bin/bash
-
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
# Usage:
# ./scripts/update_dep.sh module version
-# or ./scripts/update_dep.sh module
+# or ./scripts/update_dep.sh module (to update to the latest version)
# e.g.
# ./scripts/update_dep.sh github.com/golang/groupcache
# ./scripts/update_dep.sh github.com/soheilhy/cmux v0.1.5
@@ -13,21 +26,67 @@ set -euo pipefail
source ./scripts/test_lib.sh
+if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
+ log_error "Illegal number of parameters. Usage: $0 module [version]"
+ exit 1
+fi
+
mod="$1"
-ver="$2"
+ver="${2:-}"
+
+function print_current_dep_version {
+ log_info "${mod} version in all go.mod files:"
+ find . -name go.mod -exec grep -H "^\s*${mod}\s" {} + | sed 's|:|\t|' || true
+ printf "\n"
+}
-function maybe_update_module {
- run go mod tidy
+function is_fully_indirect {
+ local result
+ result=$(find . -name go.mod -print0 | xargs -0 -I{} /bin/sh -c "cd \$(dirname {}); go list -f \"{{if eq .Path \\\"${mod}\\\"}}{{.Indirect}}{{end}}\" -m all" | sort | uniq)
+ [ "$result" = "true" ]
+}
- deps=$(go list -f '{{if not .Indirect}}{{if .Version}}{{.Path}},{{.Version}}{{end}}{{end}}' -m all)
- if [[ "$deps" == *"${mod}"* ]]; then
+function update_module {
+ local subdir
+ subdir=$(module_subdir)
+
+ # The `go get` command is most effective on dependencies that are explicitly
+ # listed as direct requirements in the go.mod file. When updating a purely
+ # indirect dependency, `go get` might not update it as expected.
+ #
+ # To work around this, we temporarily promote the indirect dependency to a
+ # direct one in the go.mod file using `go mod edit`. This ensures that
+ # `go get` will see and correctly update the module. Subsequent cleanup
+ # commands (like `go mod tidy`) will automatically move it back
+ # to an indirect dependency, but at the designated updated version.
+ #
+ # Note: `go mod edit` requires a specific version (e.g., v1.2.3), so we only
+ # use it when a version is explicitly provided. For "latest", we skip this
+ # step and let `go get -u` handle it directly.
+ if [ -n "${ver}" ]; then
+ run go mod edit -require "${mod}@${ver}" || true
+ fi
+
+ # Check if the module is a dependency.
+ if go list -m all | grep -q -E "^\s*${mod}\s"; then
+ log_info " Updating in ${subdir}..."
if [ -z "${ver}" ]; then
- run go get "${mod}"
+ run go get -u "${mod}"
else
run go get "${mod}@${ver}"
fi
fi
- }
+}
+
+print_current_dep_version
+if is_fully_indirect; then
+ read -p "Module ${mod} is a purely indirect dependency. Are you sure you want to update it? [y/N] " -r confirm
+ [[ "$confirm" == [Yy] ]] || exit # Default is No
+fi
+
+log_info "Updating '${mod}' to ${ver:-latest} across all modules..."
+run_for_workspace_modules update_module
+
+make fix-mod-tidy fix-bom update-go-workspace verify-dep
-go mod tidy
-run_for_modules maybe_update_module
+print_current_dep_version
diff --git a/scripts/update_go_workspace.sh b/scripts/update_go_workspace.sh
new file mode 100755
index 000000000000..cc7e5c3a0b74
--- /dev/null
+++ b/scripts/update_go_workspace.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+#
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
+# Based on k/k scripts/update-go-workspace.sh:
+# https://github.com/kubernetes/kubernetes/blob/e2b96b25661849775dedf441b2f5c555392caa84/hack/update-go-workspace.sh
+
+# This script generates go.work so that it includes all Go packages
+# in this repo, with a few exceptions.
+
+set -euo pipefail
+
+source ./scripts/test_lib.sh
+
+# Detect sed variant (BSD vs GNU) for in-place editing
+# Source: https://stackoverflow.com/a/22084103 (CC BY-SA 4.0)
+case "$OSTYPE" in
+ darwin*|bsd*)
+ sed_no_backup=( -i '' )
+ ;;
+ *)
+ sed_no_backup=( -i )
+ ;;
+esac
+
+# Avoid issues and remove the workspace files.
+rm -f go.work go.work.sum
+
+# Generate the workspace.
+go work init
+# Prepend comment header (portable sed syntax with literal newline after backslash)
+sed "${sed_no_backup[@]}" '1i\
+// This is a generated file. Do not edit directly.\
+
+' go.work
+
+# Include all submodules from the repository.
+# Use while-read loop for portability (dirname -z is GNU-specific, not available on macOS)
+git ls-files -z ':(glob)**/go.mod' | while IFS= read -r -d '' modfile; do
+ go work edit -use "$(dirname "$modfile")"
+done
+
+go work edit -toolchain "go$(cat .go-version)"
+go work edit -go "$(go mod edit -json | jq -r .Go)"
+
+# generate go.work.sum
+go mod download
diff --git a/scripts/update_proto_annotations.sh b/scripts/update_proto_annotations.sh
index 75089e772221..886c259b5ea6 100755
--- a/scripts/update_proto_annotations.sh
+++ b/scripts/update_proto_annotations.sh
@@ -1,4 +1,18 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
# Updates etcd_version_annotations.txt based on state of annotations in proto files.
# Developers can run this script to avoid manually updating etcd_version_annotations.txt.
# Before running this script please ensure that fields/messages that you added are annotated with next etcd version.
diff --git a/scripts/updatebom.sh b/scripts/updatebom.sh
deleted file mode 100755
index 2c6bf8720b0d..000000000000
--- a/scripts/updatebom.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-
-set -euo pipefail
-
-source ./scripts/test_lib.sh
-
-function bom_fixlet {
- log_callout "generating bill-of-materials.json"
-
- cp go.mod go.mod.tmp
- cp go.sum go.sum.tmp
-
- local modules
- # shellcheck disable=SC2207
- modules=($(modules_for_bom))
-
- if GOFLAGS=-mod=mod run_go_tool "github.com/appscodelabs/license-bill-of-materials" \
- --override-file ./bill-of-materials.override.json \
- "${modules[@]}" > ./bill-of-materials.json.tmp; then
- cp ./bill-of-materials.json.tmp ./bill-of-materials.json
- log_success "bom refreshed"
- else
- log_error "FAIL: bom refreshing failed"
- mv go.mod.tmp go.mod
- mv go.sum.tmp go.sum
- return 2
- fi
- mv go.mod.tmp go.mod
- mv go.sum.tmp go.sum
-}
-
-function bom_fix {
- # We regenerate bom from the tests directory, as it's a module
- # that depends on all other modules, so we can generate comprehensive content.
- # TODO: Migrate to root module, when root module depends on everything (including server & tests).
- run_for_module "." bom_fixlet
-}
-
-# only build when called directly, not sourced
-if [[ "$0" =~ updatebom.sh$ ]]; then
- bom_fix
-fi
diff --git a/scripts/verify_genproto.sh b/scripts/verify_genproto.sh
index a66875657cf1..42a9227a8d9d 100755
--- a/scripts/verify_genproto.sh
+++ b/scripts/verify_genproto.sh
@@ -1,4 +1,18 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
# This scripts is automatically run by CI to prevent pull requests missing running genproto.sh
# after changing *.proto file.
diff --git a/scripts/verify_go_versions.sh b/scripts/verify_go_versions.sh
index 11d34f92a515..b9a2829f9138 100755
--- a/scripts/verify_go_versions.sh
+++ b/scripts/verify_go_versions.sh
@@ -1,5 +1,18 @@
#!/usr/bin/env bash
-
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
# This script verifies that the value of the toolchain directive in the
# go.mod files always match that of the .go-version file to ensure that
# we accidentally don't test and release with differing versions of Go.
@@ -34,13 +47,27 @@ function verify_go_versions() {
fi
}
+# Workaround to get go.work's toolchain, as go work edit -json doesn't return
+# the toolchain as of Go 1.24. When this is fixed, we can replace these two
+# checks with verify_go_versions go.work
+toolchain_version="$(grep toolchain go.work | cut -d' ' -f2)"
+if [[ "go${target_go_version}" != "${toolchain_version}" ]]; then
+ log_error "go toolchain directive out of sync for go.work, got: ${toolchain_version}"
+ toolchain_out_of_sync="true"
+fi
+go_line_version="$(go work edit -json | jq -r .Go)"
+if ! printf '%s\n' "${go_line_version}" "${target_go_version}" | sort --check=silent --version-sort; then
+ log_error "go directive in go.work is greater than maximum allowed: go${target_go_version}"
+ go_line_violation="true"
+fi
+
while read -r mod; do
verify_go_versions "${mod}";
done < <(find . -name 'go.mod')
if [[ "${toolchain_out_of_sync}" == "true" ]]; then
log_error
- log_error "Please run scripts/sync_go_toolchain_directive.sh or update .go-version to rectify this error"
+ log_error "Please run make sync-toolchain-directive or update .go-version to rectify this error"
fi
if [[ "${go_line_violation}" == "true" ]]; then
@@ -51,3 +78,5 @@ fi
if [[ "${go_line_violation}" == "true" ]] || [[ "${toolchain_out_of_sync}" == "true" ]]; then
exit 1
fi
+
+log_success "SUCCESS: Go toolchain directive in sync"
diff --git a/scripts/verify_golangci-lint_version.sh b/scripts/verify_golangci-lint_version.sh
new file mode 100755
index 000000000000..dba0afc53eba
--- /dev/null
+++ b/scripts/verify_golangci-lint_version.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+function install_golangci_lint() {
+ echo "Installing golangci-lint ${GOLANGCI_LINT_VERSION}"
+ (cd tools/mod && GOFLAGS="" GOBIN="$(go env GOPATH)/bin" go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint)
+}
+
+GOLANGCI_LINT_VERSION=$(cd tools/mod && go list -m -f '{{.Version}}' github.com/golangci/golangci-lint/v2)
+echo "golangci-lint version: $GOLANGCI_LINT_VERSION"
+GOLANGCI_LINT_PRESENT=$(which golangci-lint)
+
+if [ -z "$GOLANGCI_LINT_PRESENT" ]; then
+ echo "golangci-lint is not available"
+ install_golangci_lint
+ exit 0
+fi
+GOLANGCI_LINT_INSTALLED=v$(golangci-lint version | grep -Eo 'version [0-9.]+' | grep -Eo '[0-9.]+')
+
+if [ "$GOLANGCI_LINT_VERSION" != "$GOLANGCI_LINT_INSTALLED" ]; then
+ echo "different golangci-lint version installed: $GOLANGCI_LINT_INSTALLED"
+ install_golangci_lint
+ echo "golangci-lint version: $GOLANGCI_LINT_VERSION"
+fi
diff --git a/scripts/verify_grpc_experimental.sh b/scripts/verify_grpc_experimental.sh
new file mode 100755
index 000000000000..92ee47bad1b7
--- /dev/null
+++ b/scripts/verify_grpc_experimental.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+set -e
+
+# Ensure we are at the root of the repo
+ROOT_DIR=$(git rev-parse --show-toplevel)
+cd "${ROOT_DIR}"
+
+source ./scripts/test_lib.sh
+
+TOOL_SRC="${ETCD_ROOT_DIR}/tools/check-grpc-experimental"
+ALLOWLIST="${TOOL_SRC}/allowlist.txt"
+
+FAILURES=0
+
+for MOD_DIR in $(module_dirs); do
+ echo "------------------------------------------------"
+ echo "Checking module: ${MOD_DIR}"
+ pushd "${MOD_DIR}" > /dev/null
+ if ! go run "${TOOL_SRC}" -allow-list="${ALLOWLIST}" ./...; then
+ echo "ERROR: Experimental usage found in ${MOD_DIR}"
+ FAILURES=$((FAILURES+1))
+ fi
+ popd > /dev/null
+done
+
+echo "------------------------------------------------"
+if [ "$FAILURES" -eq 0 ]; then
+ echo "SUCCESS: No experimental gRPC APIs found in any module."
+ exit 0
+else
+ echo "FAILURE: Found experimental gRPC API usage in ${FAILURES} module(s)."
+ exit 1
+fi
\ No newline at end of file
diff --git a/scripts/verify_proto_annotations.sh b/scripts/verify_proto_annotations.sh
index 17da593baab4..6e5596fd153a 100755
--- a/scripts/verify_proto_annotations.sh
+++ b/scripts/verify_proto_annotations.sh
@@ -1,4 +1,18 @@
#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+#
# Verifies proto annotations to ensure all new proto fields and messages are annotated by comparing it with etcd_version_annotations.txt file.
# This scripts is automatically run by CI to prevent pull requests missing adding a proto annotation.
diff --git a/security/README.md b/security/README.md
index 3129778cad0a..e8b031689007 100644
--- a/security/README.md
+++ b/security/README.md
@@ -6,7 +6,7 @@ Join the [etcd-dev](https://groups.google.com/g/etcd-dev) group for emails about
We’re extremely grateful for security researchers and users that report vulnerabilities to the etcd Open Source Community. All reports are thoroughly investigated by a dedicated committee of community volunteers called [Product Security Committee](security-release-process.md#product-security-committee).
-To make a report, please email the private [etcd maintainers](mailto:etcd-maintainers@googlegroups.com) list with the security details and the details expected for [all etcd bug reports](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md).
+To make a report, please email the private [security@etcd.io](mailto:security@etcd.io) list with the security details and the details expected for [all etcd bug reports](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/reporting_bugs.md).
### When Should I Report a Vulnerability?
@@ -37,12 +37,3 @@ A public disclosure date is negotiated by the etcd Product Security Committee an
A third party security audit was performed by Trail of Bits, find the full report [here](SECURITY_AUDIT.pdf).
A third party fuzzing audit was performed by Ada Logics, find the full report [here](FUZZING_AUDIT_2022.PDF).
-## Private Distributor List
-
-This list provides actionable information regarding etcd security to multiple distributors. Members of the list may not use the information for anything other than fixing the issue for respective distribution's users. If you continue to leak information and break the policy outlined here, you will be removed from the list.
-
-### Request to Join
-
-New membership requests are sent to [etcd maintainers](mailto:etcd-maintainers@googlegroups.com).
-
-File an issue [here](https://github.com/etcd-io/etcd/issues/new?template=distributors-application.md), filling in the criteria template.
diff --git a/security/email-templates.md b/security/email-templates.md
index 8d93f76d5bf5..766613716773 100644
--- a/security/email-templates.md
+++ b/security/email-templates.md
@@ -7,6 +7,7 @@ This is a collection of email templates to handle various situations the securit
```
Subject: Upcoming security release of etcd $VERSION
To: etcd-dev@googlegroups.com
+Cc: security@etcd-io
Cc: etcd-maintainers@googlegroups.com
Hello etcd Community,
@@ -34,6 +35,7 @@ $PERSON on behalf of the etcd Product Security Committee and maintainers
```
Subject: Security release of etcd $VERSION is now available
To: etcd-dev@googlegroups.com
+Cc: security@etcd-io
Cc: etcd-maintainers@googlegroups.com
Hello etcd Community,
diff --git a/security/security-release-process.md b/security/security-release-process.md
index 78fb55dd967f..3dcb606a43b9 100644
--- a/security/security-release-process.md
+++ b/security/security-release-process.md
@@ -22,7 +22,7 @@ The PSC members will share various tasks as listed below:
### Contacting the Product Security Committee
-Contact the team by sending email to [etcd maintainers](mailto:etcd-maintainers@googlegroups.com).
+Contact the team by sending email to [security@etcd.io](mailto:security@etcd.io).
### Product Security Committee Membership
@@ -55,7 +55,7 @@ The etcd Community asks that all suspected vulnerabilities be privately and resp
### Public Disclosure Processes
-If anyone knows of a publicly disclosed security vulnerability please IMMEDIATELY email [etcd maintainers](mailto:etcd-maintainers@googlegroups.com) to inform the PSC about the vulnerability so they may start the patch, release, and communication process.
+If anyone knows of a publicly disclosed security vulnerability please IMMEDIATELY email [security@etcd.io](mailto:security@etcd.io) to inform the PSC about the vulnerability so they may start the patch, release, and communication process.
If possible the PSC will ask the person making the public report if the issue can be handled via a private disclosure process. If the reporter denies the PSC will move swiftly with the fix and release process. In extreme cases GitHub can be asked to delete the issue but this generally isn't necessary and is unlikely to make a public disclosure less damaging.
@@ -90,7 +90,7 @@ If the CVSS score is under ~4.0
Note: CVSS is convenient but imperfect. Ultimately, the PSC has discretion on classifying the severity of a vulnerability.
-The severity of the bug and related handling decisions must be discussed on the [etcd maintainers](mailto:etcd-maintainers@googlegroups.com) mailing list.
+The severity of the bug and related handling decisions must be discussed on the [security@etcd.io](mailto:security@etcd.io) mailing list.
### Fix Disclosure Process
diff --git a/server/.gomodguard.yaml b/server/.gomodguard.yaml
new file mode 100644
index 000000000000..18ffc6800c1f
--- /dev/null
+++ b/server/.gomodguard.yaml
@@ -0,0 +1,9 @@
+---
+blocked:
+ modules:
+ - go.etcd.io/etcd:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/tests/v3:
+ reason: "Forbidden dependency"
+ - go.etcd.io/etcd/v3:
+ reason: "Forbidden dependency"
diff --git a/server/LICENSE b/server/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/server/LICENSE
+++ b/server/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/server/auth/jwt.go b/server/auth/jwt.go
index 90e3a963cbee..ccfd8fe59012 100644
--- a/server/auth/jwt.go
+++ b/server/auth/jwt.go
@@ -121,7 +121,7 @@ func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64)
zap.Uint64("revision", revision),
zap.String("token-fingerprint", tokenFingerprint))
}
- return token, err
+ return token, nil
}
func newTokenProviderJWT(lg *zap.Logger, optMap map[string]string) (*tokenJWT, error) {
diff --git a/server/auth/jwt_test.go b/server/auth/jwt_test.go
index e987b94ab4af..3ecb3328f0d9 100644
--- a/server/auth/jwt_test.go
+++ b/server/auth/jwt_test.go
@@ -15,7 +15,6 @@
package auth
import (
- "context"
"fmt"
"maps"
"testing"
@@ -96,7 +95,7 @@ func testJWTInfo(t *testing.T, opts map[string]string) {
t.Fatal(err)
}
- ctx := context.TODO()
+ ctx := t.Context()
token, aerr := jwt.assign(ctx, "abc", 123)
if aerr != nil {
@@ -196,7 +195,7 @@ func TestJWTTokenWithMissingFields(t *testing.T) {
// verify the token
jwtProvider, err := newTokenProviderJWT(zap.NewNop(), optsMap)
require.NoError(t, err)
- ai, ok := jwtProvider.info(context.TODO(), token, 123)
+ ai, ok := jwtProvider.info(t.Context(), token, 123)
require.Equal(t, tc.expectValid, ok)
if ok {
diff --git a/server/auth/main_test.go b/server/auth/main_test.go
index 30ff6fb9a368..79a9d83b7d81 100644
--- a/server/auth/main_test.go
+++ b/server/auth/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 auth
diff --git a/server/auth/range_perm_cache.go b/server/auth/range_perm_cache.go
index 539ed290e681..57753efd4a1b 100644
--- a/server/auth/range_perm_cache.go
+++ b/server/auth/range_perm_cache.go
@@ -51,14 +51,14 @@ func getMergedPerms(tx UnsafeAuthReader, userName string) *unifiedRangePermissio
}
switch perm.PermType {
- case authpb.READWRITE:
+ case authpb.Permission_READWRITE:
readPerms.Insert(ivl, struct{}{})
writePerms.Insert(ivl, struct{}{})
- case authpb.READ:
+ case authpb.Permission_READ:
readPerms.Insert(ivl, struct{}{})
- case authpb.WRITE:
+ case authpb.Permission_WRITE:
writePerms.Insert(ivl, struct{}{})
}
}
@@ -84,9 +84,9 @@ func checkKeyInterval(
ivl := adt.NewBytesAffineInterval(key, rangeEnd)
switch permtyp {
- case authpb.READ:
+ case authpb.Permission_READ:
return cachedPerms.readPerms.Contains(ivl)
- case authpb.WRITE:
+ case authpb.Permission_WRITE:
return cachedPerms.writePerms.Contains(ivl)
default:
lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String()))
@@ -97,9 +97,9 @@ func checkKeyInterval(
func checkKeyPoint(lg *zap.Logger, cachedPerms *unifiedRangePermissions, key []byte, permtyp authpb.Permission_Type) bool {
pt := adt.NewBytesAffinePoint(key)
switch permtyp {
- case authpb.READ:
+ case authpb.Permission_READ:
return cachedPerms.readPerms.Intersects(pt)
- case authpb.WRITE:
+ case authpb.Permission_WRITE:
return cachedPerms.writePerms.Intersects(pt)
default:
lg.Panic("unknown auth type", zap.String("auth-type", permtyp.String()))
diff --git a/server/auth/range_perm_cache_test.go b/server/auth/range_perm_cache_test.go
index 4b39acbcb2f3..f267a7cf6381 100644
--- a/server/auth/range_perm_cache_test.go
+++ b/server/auth/range_perm_cache_test.go
@@ -77,7 +77,7 @@ func TestRangePermission(t *testing.T) {
readPerms.Insert(p, struct{}{})
}
- result := checkKeyInterval(zaptest.NewLogger(t), &unifiedRangePermissions{readPerms: readPerms}, tt.begin, tt.end, authpb.READ)
+ result := checkKeyInterval(zaptest.NewLogger(t), &unifiedRangePermissions{readPerms: readPerms}, tt.begin, tt.end, authpb.Permission_READ)
if result != tt.want {
t.Errorf("#%d: result=%t, want=%t", i, result, tt.want)
}
@@ -128,7 +128,7 @@ func TestKeyPermission(t *testing.T) {
readPerms.Insert(p, struct{}{})
}
- result := checkKeyPoint(zaptest.NewLogger(t), &unifiedRangePermissions{readPerms: readPerms}, tt.key, authpb.READ)
+ result := checkKeyPoint(zaptest.NewLogger(t), &unifiedRangePermissions{readPerms: readPerms}, tt.key, authpb.Permission_READ)
if result != tt.want {
t.Errorf("#%d: result=%t, want=%t", i, result, tt.want)
}
diff --git a/server/auth/simple_token_test.go b/server/auth/simple_token_test.go
index 13db76efe4a3..8b8340764df7 100644
--- a/server/auth/simple_token_test.go
+++ b/server/auth/simple_token_test.go
@@ -31,7 +31,7 @@ func TestSimpleTokenDisabled(t *testing.T) {
explicitlyDisabled.disable()
for _, tp := range []*tokenSimple{initialState, explicitlyDisabled} {
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
token, err := tp.assign(ctx, "user1", 0)
if err != nil {
t.Fatal(err)
@@ -51,7 +51,7 @@ func TestSimpleTokenAssign(t *testing.T) {
tp := newTokenProviderSimple(zaptest.NewLogger(t), dummyIndexWaiter, simpleTokenTTLDefault)
tp.enable()
defer tp.disable()
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
token, err := tp.assign(ctx, "user1", 0)
if err != nil {
t.Fatal(err)
@@ -63,7 +63,7 @@ func TestSimpleTokenAssign(t *testing.T) {
tp.invalidateUser("user1")
- _, ok = tp.info(context.TODO(), token, 0)
+ _, ok = tp.info(t.Context(), token, 0)
if ok {
t.Errorf("expected ok == false after user is invalidated")
}
diff --git a/server/auth/store.go b/server/auth/store.go
index b31fee3effa0..49cc4d06dd48 100644
--- a/server/auth/store.go
+++ b/server/auth/store.go
@@ -41,7 +41,7 @@ import (
var _ AuthStore = (*authStore)(nil)
var (
- rootPerm = authpb.Permission{PermType: authpb.READWRITE, Key: []byte{}, RangeEnd: []byte{0}}
+ rootPerm = authpb.Permission{PermType: authpb.Permission_READWRITE, Key: []byte{}, RangeEnd: []byte{0}}
ErrRootUserNotExist = errors.New("auth: root user does not exist")
ErrRootRoleNotExist = errors.New("auth: root user does not have root role")
@@ -899,15 +899,15 @@ func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeE
}
func (as *authStore) IsPutPermitted(authInfo *AuthInfo, key []byte) error {
- return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.WRITE)
+ return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.Permission_WRITE)
}
func (as *authStore) IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
- return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.READ)
+ return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.Permission_READ)
}
func (as *authStore) IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error {
- return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.WRITE)
+ return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.Permission_WRITE)
}
func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error {
@@ -1072,7 +1072,7 @@ func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) {
return nil, nil
}
- token := ts[0]
+ token := strings.TrimPrefix(ts[0], "Bearer ")
authInfo, uok := as.authInfoFromToken(ctx, token)
if !uok {
tokenFingerprint := redactToken(token)
diff --git a/server/auth/store_test.go b/server/auth/store_test.go
index bf708805cd63..fae37161d014 100644
--- a/server/auth/store_test.go
+++ b/server/auth/store_test.go
@@ -280,7 +280,7 @@ func TestUserChangePassword(t *testing.T) {
as, tearDown := setupAuthStore(t)
defer tearDown(t)
- ctx1 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx1 := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err := as.Authenticate(ctx1, "foo", "bar")
if err != nil {
t.Fatal(err)
@@ -291,7 +291,7 @@ func TestUserChangePassword(t *testing.T) {
t.Fatal(err)
}
- ctx2 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx2 := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err = as.Authenticate(ctx2, "foo", "baz")
if err != nil {
t.Fatal(err)
@@ -380,7 +380,7 @@ func TestIsOpPermitted(t *testing.T) {
}
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
}
@@ -473,7 +473,7 @@ func TestRoleGrantPermission(t *testing.T) {
}
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
}
@@ -526,7 +526,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
},
@@ -535,7 +535,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: nil key",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: nil,
RangeEnd: []byte("RangeEnd"),
},
@@ -544,7 +544,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range: single key",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: nil,
},
@@ -553,7 +553,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range: single key",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte{},
},
@@ -562,7 +562,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: empty (Key == RangeEnd)",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("a"),
RangeEnd: []byte("a"),
},
@@ -571,7 +571,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: empty (Key > RangeEnd)",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("b"),
RangeEnd: []byte("a"),
},
@@ -580,7 +580,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: length of key is 0",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte(""),
RangeEnd: []byte("a"),
},
@@ -589,7 +589,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: length of key is 0",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte(""),
RangeEnd: []byte(""),
},
@@ -598,7 +598,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "invalid range: length of key is 0",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte(""),
RangeEnd: []byte{0x00},
},
@@ -607,7 +607,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range: single key permission for []byte{0x00}",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte{0x00},
RangeEnd: []byte(""),
},
@@ -616,7 +616,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range: \"a\" or larger keys",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("a"),
RangeEnd: []byte{0x00},
},
@@ -625,7 +625,7 @@ func TestRoleGrantInvalidPermission(t *testing.T) {
{
name: "valid range: the entire keys",
perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte{0x00},
RangeEnd: []byte{0x00},
},
@@ -652,7 +652,7 @@ func TestRootRoleGrantPermission(t *testing.T) {
defer tearDown(t)
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
}
@@ -671,7 +671,7 @@ func TestRootRoleGrantPermission(t *testing.T) {
// whatever grant permission to root, it always return root permission.
expectPerm := &authpb.Permission{
- PermType: authpb.READWRITE,
+ PermType: authpb.Permission_READWRITE,
Key: []byte{},
RangeEnd: []byte{0},
}
@@ -689,7 +689,7 @@ func TestRoleRevokePermission(t *testing.T) {
}
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
}
@@ -746,7 +746,7 @@ func TestUserRevokePermission(t *testing.T) {
}
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("WriteKeyBegin"),
RangeEnd: []byte("WriteKeyEnd"),
}
@@ -811,38 +811,38 @@ func TestAuthInfoFromCtx(t *testing.T) {
as, tearDown := setupAuthStore(t)
defer tearDown(t)
- ctx := context.Background()
+ ctx := t.Context()
ai, err := as.AuthInfoFromCtx(ctx)
if err != nil && ai != nil {
t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
}
// as if it came from RPC
- ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{"tokens": "dummy"}))
+ ctx = metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{"tokens": "dummy"}))
ai, err = as.AuthInfoFromCtx(ctx)
if err != nil && ai != nil {
t.Errorf("expected (nil, nil), got (%v, %v)", ai, err)
}
- ctx = context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx = context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
resp, err := as.Authenticate(ctx, "foo", "bar")
if err != nil {
t.Error(err)
}
- ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid Token"}))
+ ctx = metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid Token"}))
_, err = as.AuthInfoFromCtx(ctx)
if !errors.Is(err, ErrInvalidAuthToken) {
t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
}
- ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid.Token"}))
+ ctx = metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Invalid.Token"}))
_, err = as.AuthInfoFromCtx(ctx)
if !errors.Is(err, ErrInvalidAuthToken) {
t.Errorf("expected %v, got %v", ErrInvalidAuthToken, err)
}
- ctx = metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: resp.Token}))
+ ctx = metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: resp.Token}))
ai, err = as.AuthInfoFromCtx(ctx)
if err != nil {
t.Error(err)
@@ -850,6 +850,11 @@ func TestAuthInfoFromCtx(t *testing.T) {
if ai.Username != "foo" {
t.Errorf("expected %v, got %v", "foo", ai.Username)
}
+
+ ctx = metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "Bearer " + resp.Token}))
+ ai, err = as.AuthInfoFromCtx(ctx)
+ require.NoError(t, err)
+ require.Equal(t, "foo", ai.Username)
}
func TestAuthDisable(t *testing.T) {
@@ -857,7 +862,7 @@ func TestAuthDisable(t *testing.T) {
defer tearDown(t)
as.AuthDisable()
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err := as.Authenticate(ctx, "foo", "bar")
if !errors.Is(err, ErrAuthNotEnabled) {
t.Errorf("expected %v, got %v", ErrAuthNotEnabled, err)
@@ -879,7 +884,7 @@ func TestIsAuthEnabled(t *testing.T) {
as.AuthEnable()
status := as.IsAuthEnabled()
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, _ = as.Authenticate(ctx, "foo", "bar")
if status != true {
t.Errorf("expected %v, got %v", true, false)
@@ -907,7 +912,7 @@ func TestAuthInfoFromCtxRace(t *testing.T) {
donec := make(chan struct{})
go func() {
defer close(donec)
- ctx := metadata.NewIncomingContext(context.Background(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "test"}))
+ ctx := metadata.NewIncomingContext(t.Context(), metadata.New(map[string]string{rpctypes.TokenFieldNameGRPC: "test"}))
as.AuthInfoFromCtx(ctx)
}()
as.UserAdd(&pb.AuthUserAddRequest{Name: "test", Options: &authpb.UserAddOptions{NoPassword: false}})
@@ -1025,7 +1030,7 @@ func TestHammerSimpleAuthenticate(t *testing.T) {
go func(user string) {
defer wg.Done()
token := fmt.Sprintf("%s(%d)", user, i)
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, token)
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, token)
if _, err := as.Authenticate(ctx, user, "123"); err != nil {
t.Error(err)
}
@@ -1106,7 +1111,7 @@ func testAuthInfoFromCtxWithRoot(t *testing.T, opts string) {
t.Fatal(err)
}
- ctx := context.Background()
+ ctx := t.Context()
ctx = as.WithRoot(ctx)
ai, aerr := as.AuthInfoFromCtx(ctx)
@@ -1130,7 +1135,7 @@ func TestUserNoPasswordAdd(t *testing.T) {
t.Fatal(err)
}
- ctx := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err = as.Authenticate(ctx, username, "")
require.ErrorIsf(t, err, ErrAuthFailed, "expected %v, got %v", ErrAuthFailed, err)
}
@@ -1150,7 +1155,7 @@ func TestUserChangePasswordWithOldLog(t *testing.T) {
as, tearDown := setupAuthStore(t)
defer tearDown(t)
- ctx1 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx1 := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(1)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err := as.Authenticate(ctx1, "foo", "bar")
if err != nil {
t.Fatal(err)
@@ -1161,7 +1166,7 @@ func TestUserChangePasswordWithOldLog(t *testing.T) {
t.Fatal(err)
}
- ctx2 := context.WithValue(context.WithValue(context.TODO(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
+ ctx2 := context.WithValue(context.WithValue(t.Context(), AuthenticateParamIndex{}, uint64(2)), AuthenticateParamSimpleTokenPrefix{}, "dummy")
_, err = as.Authenticate(ctx2, "foo", "baz")
if err != nil {
t.Fatal(err)
diff --git a/server/config/config.go b/server/config/config.go
index 3bf994b7a9f2..2d5513395d08 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -42,9 +42,7 @@ const (
type ServerConfig struct {
Name string
- DiscoveryURL string
- DiscoveryProxy string
- DiscoveryCfg v3discovery.DiscoveryConfig
+ DiscoveryCfg v3discovery.DiscoveryConfig
ClientURLs types.URLs
PeerURLs types.URLs
@@ -188,12 +186,6 @@ type ServerConfig struct {
// be refined to mlock in-use area of bbolt only.
MemoryMlock bool `json:"memory-mlock"`
- // ExperimentalTxnModeWriteWithSharedBuffer enable write transaction to use
- // a shared buffer in its readonly check operations.
- // TODO: Delete in v3.7
- // Deprecated: Use TxnModeWriteWithSharedBuffer Feature Gate instead. Will be decommissioned in v3.7.
- ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"`
-
// BootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
// consider running defrag during bootstrap. Needs to be set to non-zero value to take effect.
BootstrapDefragThresholdMegabytes uint `json:"bootstrap-defrag-threshold-megabytes"`
@@ -204,8 +196,8 @@ type ServerConfig struct {
// V2Deprecation defines a phase of v2store deprecation process.
V2Deprecation V2DeprecationEnum `json:"v2-deprecation"`
- // ExperimentalLocalAddress is the local IP address to use when communicating with a peer.
- ExperimentalLocalAddress string `json:"experimental-local-address"`
+ // LocalAddress is the local IP address to use when communicating with a peer.
+ LocalAddress string `json:"local-address"`
// ServerFeatureGate is a server level feature gate
ServerFeatureGate featuregate.FeatureGate
@@ -226,8 +218,8 @@ func (c *ServerConfig) VerifyBootstrap() error {
if CheckDuplicateURL(c.InitialPeerURLsMap) {
return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
}
- if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
- return fmt.Errorf("initial cluster unset and no discovery URL found")
+ if c.InitialPeerURLsMap.String() == "" && !c.ShouldDiscover() {
+ return fmt.Errorf("initial cluster unset and no discovery endpoints found")
}
return nil
}
@@ -243,7 +235,7 @@ func (c *ServerConfig) VerifyJoinExisting() error {
if CheckDuplicateURL(c.InitialPeerURLsMap) {
return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
}
- if c.DiscoveryURL != "" {
+ if c.ShouldDiscover() {
return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
}
return nil
@@ -321,7 +313,7 @@ func (c *ServerConfig) WALDir() string {
func (c *ServerConfig) SnapDir() string { return filepath.Join(c.MemberDir(), "snap") }
func (c *ServerConfig) ShouldDiscover() bool {
- return c.DiscoveryURL != "" || len(c.DiscoveryCfg.Endpoints) > 0
+ return len(c.DiscoveryCfg.Endpoints) > 0
}
// ReqTimeout returns timeout for request to finish.
diff --git a/server/config/config_test.go b/server/config/config_test.go
index b1ae74b789f5..8fa22b289299 100644
--- a/server/config/config_test.go
+++ b/server/config/config_test.go
@@ -22,6 +22,8 @@ import (
"go.uber.org/zap/zaptest"
"go.etcd.io/etcd/client/pkg/v3/types"
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/v3discovery"
)
func mustNewURLs(t *testing.T, urls []string) []url.URL {
@@ -33,10 +35,14 @@ func mustNewURLs(t *testing.T, urls []string) []url.URL {
return u
}
-func TestConfigVerifyBootstrapWithoutClusterAndDiscoveryURLFail(t *testing.T) {
+func TestConfigVerifyBootstrapWithoutClusterFail(t *testing.T) {
c := &ServerConfig{
- Name: "node1",
- DiscoveryURL: "",
+ Name: "node1",
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: []string{},
+ },
+ },
InitialPeerURLsMap: types.URLsMap{},
Logger: zaptest.NewLogger(t),
}
@@ -49,8 +55,12 @@ func TestConfigVerifyExistingWithDiscoveryURLFail(t *testing.T) {
cluster, err := types.NewURLsMap("node1=http://127.0.0.1:2380")
require.NoErrorf(t, err, "NewCluster error: %v", err)
c := &ServerConfig{
- Name: "node1",
- DiscoveryURL: "http://127.0.0.1:2379/abcdefg",
+ Name: "node1",
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: []string{"http://192.168.0.100:2379"},
+ },
+ },
PeerURLs: mustNewURLs(t, []string{"http://127.0.0.1:2380"}),
InitialPeerURLsMap: cluster,
NewCluster: false,
@@ -196,9 +206,17 @@ func TestShouldDiscover(t *testing.T) {
"http://discovery.etcd.io/asdf": true,
}
for durl, w := range tests {
+ var eps []string
+ if durl != "" {
+ eps = append(eps, durl)
+ }
cfg := ServerConfig{
- DiscoveryURL: durl,
- Logger: zaptest.NewLogger(t),
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: eps,
+ },
+ },
+ Logger: zaptest.NewLogger(t),
}
if g := cfg.ShouldDiscover(); g != w {
t.Errorf("durl=%q: ShouldDiscover()=%t, want=%t", durl, g, w)
diff --git a/server/config/v2_deprecation.go b/server/config/v2_deprecation.go
index c50401cc937a..4913fd881715 100644
--- a/server/config/v2_deprecation.go
+++ b/server/config/v2_deprecation.go
@@ -21,48 +21,28 @@ const (
// Default in v3.5, and no longer supported in v3.6.
V2Depr0NotYet = V2DeprecationEnum("not-yet")
- // Deprecated: to be decommissioned in 3.7. Please use V2Depr0NotYet.
- // TODO: remove in 3.7
- //revive:disable-next-line:var-naming
- V2_DEPR_0_NOT_YET = V2Depr0NotYet
-
// V2Depr1WriteOnly means only writing v2store is allowed.
// Default in v3.6. Meaningful v2 state is not allowed.
// The V2 files are maintained for v3.5 rollback.
V2Depr1WriteOnly = V2DeprecationEnum("write-only")
- // Deprecated: to be decommissioned in 3.7. Please use V2Depr1WriteOnly.
- // TODO: remove in 3.7
- //revive:disable-next-line:var-naming
- V2_DEPR_1_WRITE_ONLY = V2Depr1WriteOnly
+ // V2Depr1WriteOnlySkipCheck is like V2Depr1WriteOnly, but bypasses the v2 content check.
+ // Use only for 3.5 -> 3.6 upgrades with existing v2 data.
+ // WARNING: Users should read the 3.5 -> 3.6 upgrade guide and use this option at their own risk
+ V2Depr1WriteOnlySkipCheck = V2DeprecationEnum("write-only-skip-check")
// V2Depr1WriteOnlyDrop means v2store is WIPED if found !!!
// Will be default in 3.7.
V2Depr1WriteOnlyDrop = V2DeprecationEnum("write-only-drop-data")
- // Deprecated: to be decommissioned in 3.7. Pleae use V2Depr1WriteOnlyDrop.
- // TODO: remove in 3.7
- //revive:disable-next-line:var-naming
- V2_DEPR_1_WRITE_ONLY_DROP = V2Depr1WriteOnlyDrop
-
// V2Depr2Gone means v2store is completely gone. The v2store is
// neither written nor read. Anything related to v2store will be
// cleaned up in v3.8. Usage of this configuration is blocking
// ability to rollback to etcd v3.5.
V2Depr2Gone = V2DeprecationEnum("gone")
- // Deprecated: to be decommissioned in 3.7. Please use V2Depr2Gone.
- // TODO: remove in 3.7
- //revive:disable-next-line:var-naming
- V2_DEPR_2_GONE = V2Depr2Gone
-
// V2DeprDefault is the default deprecation level.
V2DeprDefault = V2Depr1WriteOnly
-
- // Deprecated: to be decommissioned in 3.7. Please use V2DeprDefault.
- // TODO: remove in 3.7
- //revive:disable-next-line:var-naming
- V2_DEPR_DEFAULT = V2DeprDefault
)
func (e V2DeprecationEnum) IsAtLeast(v2d V2DeprecationEnum) bool {
@@ -73,7 +53,7 @@ func (e V2DeprecationEnum) level() int {
switch e {
case V2Depr0NotYet:
return 0
- case V2Depr1WriteOnly:
+ case V2Depr1WriteOnly, V2Depr1WriteOnlySkipCheck:
return 1
case V2Depr1WriteOnlyDrop:
return 2
diff --git a/server/embed/auth_test.go b/server/embed/auth_test.go
index a09e618f66c8..dc92d92e5894 100644
--- a/server/embed/auth_test.go
+++ b/server/embed/auth_test.go
@@ -15,7 +15,6 @@
package embed
import (
- "context"
"testing"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3client"
@@ -24,6 +23,10 @@ import (
func TestEnableAuth(t *testing.T) {
tdir := t.TempDir()
cfg := NewConfig()
+
+ testURLConfig := newConfigTestURLs()
+ applyTestURLConfig(cfg, testURLConfig)
+
cfg.Dir = tdir
e, err := StartEtcd(cfg)
if err != nil {
@@ -33,19 +36,19 @@ func TestEnableAuth(t *testing.T) {
client := v3client.New(e.Server)
defer client.Close()
- _, err = client.RoleAdd(context.TODO(), "root")
+ _, err = client.RoleAdd(t.Context(), "root")
if err != nil {
t.Fatal(err)
}
- _, err = client.UserAdd(context.TODO(), "root", "root")
+ _, err = client.UserAdd(t.Context(), "root", "root")
if err != nil {
t.Fatal(err)
}
- _, err = client.UserGrantRole(context.TODO(), "root", "root")
+ _, err = client.UserGrantRole(t.Context(), "root", "root")
if err != nil {
t.Fatal(err)
}
- _, err = client.AuthEnable(context.TODO())
+ _, err = client.AuthEnable(t.Context())
if err != nil {
t.Fatal(err)
}
diff --git a/server/embed/config.go b/server/embed/config.go
index d61afc3cbea2..b66a5d98e9c8 100644
--- a/server/embed/config.go
+++ b/server/embed/config.go
@@ -101,21 +101,11 @@ const (
// Compress = false // compress the rotated log in gzip format
DefaultLogRotationConfig = `{"maxsize": 100, "maxage": 0, "maxbackups": 0, "localtime": false, "compress": false}`
- // ExperimentalDistributedTracingAddress is the default collector address.
- // TODO: delete in v3.7
- // Deprecated: Use DefaultDistributedTracingAddress instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingAddress = "localhost:4317"
// DefaultDistributedTracingAddress is the default collector address.
DefaultDistributedTracingAddress = "localhost:4317"
- // ExperimentalDistributedTracingServiceName is the default etcd service name.
- // TODO: delete in v3.7
- // Deprecated: Use DefaultDistributedTracingServiceName instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingServiceName = "etcd"
// DefaultDistributedTracingServiceName is the default etcd service name.
DefaultDistributedTracingServiceName = "etcd"
- DefaultExperimentalTxnModeWriteWithSharedBuffer = true
-
// DefaultStrictReconfigCheck is the default value for "--strict-reconfig-check" flag.
// It's enabled by default.
DefaultStrictReconfigCheck = true
@@ -131,7 +121,7 @@ const (
var (
ErrConflictBootstrapFlags = fmt.Errorf("multiple discovery or bootstrap flags are set. " +
- "Choose one of \"initial-cluster\", \"discovery\", \"discovery-endpoints\" or \"discovery-srv\"")
+ "Choose one of \"initial-cluster\", \"discovery-endpoints\" or \"discovery-srv\"")
ErrUnsetAdvertiseClientURLsFlag = fmt.Errorf("--advertise-client-urls is required when --listen-client-urls is set explicitly")
ErrLogRotationInvalidLogOutput = fmt.Errorf("--log-outputs requires a single file path when --log-rotate-config-json is defined")
@@ -143,28 +133,6 @@ var (
// indirection for testing
getCluster = srv.GetCluster
-
- // in 3.6, we are migration all the --experimental flags to feature gate and flags without the prefix.
- // This is the mapping from the non boolean `experimental-` to the new flags.
- // TODO: delete in v3.7
- experimentalFlagMigrationMap = map[string]string{
- "experimental-compact-hash-check-time": "compact-hash-check-time",
- "experimental-corrupt-check-time": "corrupt-check-time",
- "experimental-compaction-batch-limit": "compaction-batch-limit",
- "experimental-watch-progress-notify-interval": "watch-progress-notify-interval",
- "experimental-warning-apply-duration": "warning-apply-duration",
- "experimental-bootstrap-defrag-threshold-megabytes": "bootstrap-defrag-threshold-megabytes",
- "experimental-memory-mlock": "memory-mlock",
- "experimental-snapshot-catchup-entries": "snapshot-catchup-entries",
- "experimental-compaction-sleep-interval": "compaction-sleep-interval",
- "experimental-downgrade-check-time": "downgrade-check-time",
- "experimental-peer-skip-client-san-verification": "peer-skip-client-san-verification",
- "experimental-enable-distributed-tracing": "enable-distributed-tracing",
- "experimental-distributed-tracing-address": "distributed-tracing-address",
- "experimental-distributed-tracing-service-name": "distributed-tracing-service-name",
- "experimental-distributed-tracing-instance-id": "distributed-tracing-instance-id",
- "experimental-distributed-tracing-sampling-rate": "distributed-tracing-sampling-rate",
- }
)
var (
@@ -189,6 +157,13 @@ func init() {
}
// Config holds the arguments for configuring an etcd server.
+//
+// Note: time.Duration fields in this struct are unmarshalled from JSON/YAML
+// as integer nanoseconds (e.g. 600000000000 for 10 minutes). Human-readable
+// duration strings such as "10m" are only accepted by the equivalent
+// command-line flags, not by the configuration file.
+// This is a [known Go standard library limitation](https://github.com/golang/go/issues/10275)
+// where time.Duration is unmarshaled as a plain integer.
type Config struct {
Name string `json:"name"`
Dir string `json:"data-dir"`
@@ -198,17 +173,6 @@ type Config struct {
// SnapshotCount is the number of committed transactions that trigger a snapshot.
SnapshotCount uint64 `json:"snapshot-count"`
- // ExperimentalSnapshotCatchUpEntries is the number of entries for a slow follower
- // to catch-up after compacting the raft storage entries.
- // We expect the follower has a millisecond level latency with the leader.
- // The max throughput is around 10K. Keep a 5K entries is enough for helping
- // follower to catch up.
- // TODO: remove in v3.7.
- // Note we made a mistake in https://github.com/etcd-io/etcd/pull/15033. The json tag
- // `*-catch-up-*` isn't consistent with the command line flag `*-catchup-*`.
- // Deprecated: Use SnapshotCatchUpEntries instead. Will be removed in v3.7.
- ExperimentalSnapshotCatchUpEntries uint64 `json:"experimental-snapshot-catch-up-entries"`
-
// SnapshotCatchUpEntries is the number of entires for a slow follower
// to catch-up after compacting the raft storage entries.
// We expect the follower has a millisecond level latency with the leader.
@@ -303,9 +267,7 @@ type Config struct {
ClusterState string `json:"initial-cluster-state"`
DNSCluster string `json:"discovery-srv"`
DNSClusterServiceName string `json:"discovery-srv-name"`
- Dproxy string `json:"discovery-proxy"`
- Durl string `json:"discovery"`
DiscoveryCfg v3discovery.DiscoveryConfig `json:"discovery-config"`
InitialCluster string `json:"initial-cluster"`
@@ -399,73 +361,24 @@ type Config struct {
// AuthTokenTTL in seconds of the simple token
AuthTokenTTL uint `json:"auth-token-ttl"`
- // ExperimentalInitialCorruptCheck defines to check data corrution on boot.
- // TODO: delete in v3.7
- // Deprecated: Use InitialCorruptCheck Feature Gate instead. Will be decommissioned in v3.7.
- ExperimentalInitialCorruptCheck bool `json:"experimental-initial-corrupt-check"`
- // ExperimentalCorruptCheckTime is the duration of time between cluster corruption check passes.
- // TODO: delete in v3.7
- // Deprecated: Use CorruptCheckTime instead. Will be decommissioned in v3.7.
- ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time"`
// CorruptCheckTime is the duration of time between cluster corruption check passes.
CorruptCheckTime time.Duration `json:"corrupt-check-time"`
- // ExperimentalCompactHashCheckEnabled enables leader to periodically check followers compaction hashes.
- // TODO: delete in v3.7
- // Deprecated: Use CompactHashCheck Feature Gate. Will be decommissioned in v3.7.
- ExperimentalCompactHashCheckEnabled bool `json:"experimental-compact-hash-check-enabled"`
- // ExperimentalCompactHashCheckTime is the duration of time between leader checks followers compaction hashes.
- // TODO: delete in v3.7
- // Deprecated: Use CompactHashCheckTime instead. Will be decommissioned in v3.7.
- ExperimentalCompactHashCheckTime time.Duration `json:"experimental-compact-hash-check-time"`
+
// CompactHashCheckTime is the duration of time between leader checks followers compaction hashes.
CompactHashCheckTime time.Duration `json:"compact-hash-check-time"`
-
- // ExperimentalEnableLeaseCheckpoint enables leader to send regular checkpoints to other members to prevent reset of remaining TTL on leader change.
- ExperimentalEnableLeaseCheckpoint bool `json:"experimental-enable-lease-checkpoint"`
- // ExperimentalEnableLeaseCheckpointPersist enables persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled.
- // Requires experimental-enable-lease-checkpoint to be enabled.
- // TODO: Delete in v3.7
- // Deprecated: To be decommissioned in v3.7.
- ExperimentalEnableLeaseCheckpointPersist bool `json:"experimental-enable-lease-checkpoint-persist"`
- // ExperimentalCompactionBatchLimit Sets the maximum revisions deleted in each compaction batch.
- // TODO: Delete in v3.7
- // Deprecated: Use CompactionBatchLimit instead. Will be decommissioned in v3.7.
- ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit"`
// CompactionBatchLimit Sets the maximum revisions deleted in each compaction batch.
CompactionBatchLimit int `json:"compaction-batch-limit"`
- // ExperimentalCompactionSleepInterval is the sleep interval between every etcd compaction loop.
- // TODO: Delete in v3.7
- // Deprecated: Use CompactionSleepInterval instead. Will be decommissioned in v3.7.
- ExperimentalCompactionSleepInterval time.Duration `json:"experimental-compaction-sleep-interval"`
// CompactionSleepInterval is the sleep interval between every etcd compaction loop.
CompactionSleepInterval time.Duration `json:"compaction-sleep-interval"`
- // ExperimentalWatchProgressNotifyInterval is the time duration of periodic watch progress notifications.
- // TODO: Delete in v3.7
- // Deprecated: Use WatchProgressNotifyInterval instead. Will be decommissioned in v3.7.
- ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval"`
// WatchProgressNotifyInterval is the time duration of periodic watch progress notifications.
WatchProgressNotifyInterval time.Duration `json:"watch-progress-notify-interval"`
- // ExperimentalWarningApplyDuration is the time duration after which a warning is generated if applying request
- // takes more time than this value.
- // TODO: Delete in v3.7
- // Deprecated: Use WarningApplyDuration instead. Will be decommissioned in v3.7.
- ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration"`
// WarningApplyDuration is the time duration after which a warning is generated if applying request
WarningApplyDuration time.Duration `json:"warning-apply-duration"`
- // ExperimentalBootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
- // consider running defrag during bootstrap. Needs to be set to non-zero value to take effect.
- // TODO: Delete in v3.7
- // Deprecated: Use BootstrapDefragThresholdMegabytes instead. Will be decommissioned in v3.7.
- ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes"`
// BootstrapDefragThresholdMegabytes is the minimum number of megabytes needed to be freed for etcd server to
BootstrapDefragThresholdMegabytes uint `json:"bootstrap-defrag-threshold-megabytes"`
// WarningUnaryRequestDuration is the time duration after which a warning is generated if applying
// unary request takes more time than this value.
WarningUnaryRequestDuration time.Duration `json:"warning-unary-request-duration"`
- // ExperimentalWarningUnaryRequestDuration is the time duration after which a warning is generated if applying
- // TODO: Delete in v3.7
- // Deprecated: Use WarningUnaryRequestDuration. Will be decommissioned in v3.7.
- ExperimentalWarningUnaryRequestDuration time.Duration `json:"experimental-warning-unary-request-duration"`
// MaxLearners sets a limit to the number of learner members that can exist in the cluster membership.
MaxLearners int `json:"max-learners"`
@@ -477,55 +390,23 @@ type Config struct {
ListenMetricsUrls []url.URL
ListenMetricsUrlsJSON string `json:"listen-metrics-urls"`
- // ExperimentalEnableDistributedTracing indicates if experimental tracing using OpenTelemetry is enabled.
- // TODO: delete in v3.7
- // Deprecated: Use EnableDistributedTracing instead. Will be decommissioned in v3.7.
- ExperimentalEnableDistributedTracing bool `json:"experimental-enable-distributed-tracing"`
// EnableDistributedTracing indicates if tracing using OpenTelemetry is enabled.
EnableDistributedTracing bool `json:"enable-distributed-tracing"`
- // ExperimentalDistributedTracingAddress is the address of the OpenTelemetry Collector.
- // Can only be set if ExperimentalEnableDistributedTracing is true.
- // TODO: delete in v3.7
- // Deprecated: Use DistributedTracingAddress instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingAddress string `json:"experimental-distributed-tracing-address"`
// DistributedTracingAddress is the address of the OpenTelemetry Collector.
// Can only be set if EnableDistributedTracing is true.
DistributedTracingAddress string `json:"distributed-tracing-address"`
- // ExperimentalDistributedTracingServiceName is the name of the service.
- // Can only be used if ExperimentalEnableDistributedTracing is true.
- // TODO: delete in v3.7
- // Deprecated: Use DistributedTracingServiceName instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingServiceName string `json:"experimental-distributed-tracing-service-name"`
// DistributedTracingServiceName is the name of the service.
// Can only be used if EnableDistributedTracing is true.
DistributedTracingServiceName string `json:"distributed-tracing-service-name"`
- // ExperimentalDistributedTracingServiceInstanceID is the ID key of the service.
- // This ID must be unique, as helps to distinguish instances of the same service
- // that exist at the same time.
- // Can only be used if ExperimentalEnableDistributedTracing is true.
- // TODO: delete in v3.7
- // Deprecated: Use DistributedTracingServiceInstanceID instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingServiceInstanceID string `json:"experimental-distributed-tracing-instance-id"`
// DistributedTracingServiceInstanceID is the ID key of the service.
// This ID must be unique, as helps to distinguish instances of the same service
// that exist at the same time.
// Can only be used if EnableDistributedTracing is true.
DistributedTracingServiceInstanceID string `json:"distributed-tracing-instance-id"`
- // ExperimentalDistributedTracingSamplingRatePerMillion is the number of samples to collect per million spans.
- // Defaults to 0.
- // TODO: delete in v3.7
- // Deprecated: Use DistributedTracingSamplingRatePerMillion instead. Will be decommissioned in v3.7.
- ExperimentalDistributedTracingSamplingRatePerMillion int `json:"experimental-distributed-tracing-sampling-rate"`
// DistributedTracingSamplingRatePerMillion is the number of samples to collect per million spans.
// Defaults to 0.
DistributedTracingSamplingRatePerMillion int `json:"distributed-tracing-sampling-rate"`
- // ExperimentalPeerSkipClientSanVerification determines whether to skip verification of SAN field
- // in client certificate for peer connections.
- // TODO: Delete in v3.7
- // Deprecated: Use `peer-skip-client-san-verification` instead. Will be decommissioned in v3.7.
- ExperimentalPeerSkipClientSanVerification bool `json:"experimental-peer-skip-client-san-verification"`
-
// Logger is logger options: currently only supports "zap".
// "capnslog" is removed in v3.5.
Logger string `json:"logger"`
@@ -560,10 +441,6 @@ type Config struct {
// Setting this is unsafe and will cause data loss.
UnsafeNoFsync bool `json:"unsafe-no-fsync"`
- // ExperimentalDowngradeCheckTime is the duration between two downgrade status checks (in seconds).
- // TODO: Delete `ExperimentalDowngradeCheckTime` in v3.7.
- // Deprecated: Use DowngradeCheckTime instead. Will be decommissioned in v3.7.
- ExperimentalDowngradeCheckTime time.Duration `json:"experimental-downgrade-check-time"`
// DowngradeCheckTime is the duration between two downgrade status checks (in seconds).
DowngradeCheckTime time.Duration `json:"downgrade-check-time"`
@@ -575,21 +452,6 @@ type Config struct {
// be refined to mlock in-use area of bbolt only.
MemoryMlock bool `json:"memory-mlock"`
- // ExperimentalMemoryMlock enables mlocking of etcd owned memory pages.
- // TODO: Delete in v3.7
- // Deprecated: Use MemoryMlock instad. To be decommissioned in v3.7.
- ExperimentalMemoryMlock bool `json:"experimental-memory-mlock"`
-
- // ExperimentalTxnModeWriteWithSharedBuffer enables write transaction to use a shared buffer in its readonly check operations.
- // TODO: Delete in v3.7
- // Deprecated: Use TxnModeWriteWithSharedBuffer Feature Flag. Will be decommissioned in v3.7.
- ExperimentalTxnModeWriteWithSharedBuffer bool `json:"experimental-txn-mode-write-with-shared-buffer"`
-
- // ExperimentalStopGRPCServiceOnDefrag enables etcd gRPC service to stop serving client requests on defragmentation.
- // TODO: Delete in v3.7
- // Deprecated: Use StopGRPCServiceOnDefrag Feature Flag. Will be decommissioned in v3.7.
- ExperimentalStopGRPCServiceOnDefrag bool `json:"experimental-stop-grpc-service-on-defrag"`
-
// V2Deprecation describes phase of API & Storage V2 support.
// Do not set this field for embedded use cases, as it has no effect. However, setting it will not cause any harm.
// TODO: Delete in v3.8
@@ -650,14 +512,14 @@ func NewConfig() *Config {
Name: DefaultName,
- SnapshotCount: etcdserver.DefaultSnapshotCount,
- ExperimentalSnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
- SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
+ SnapshotCount: etcdserver.DefaultSnapshotCount,
+ SnapshotCatchUpEntries: etcdserver.DefaultSnapshotCatchUpEntries,
- MaxTxnOps: DefaultMaxTxnOps,
- MaxRequestBytes: DefaultMaxRequestBytes,
- MaxConcurrentStreams: DefaultMaxConcurrentStreams,
- WarningApplyDuration: DefaultWarningApplyDuration,
+ MaxTxnOps: DefaultMaxTxnOps,
+ MaxRequestBytes: DefaultMaxRequestBytes,
+ MaxConcurrentStreams: DefaultMaxConcurrentStreams,
+ WarningApplyDuration: DefaultWarningApplyDuration,
+ WarningUnaryRequestDuration: DefaultWarningUnaryRequestDuration,
GRPCKeepAliveMinTime: DefaultGRPCKeepAliveMinTime,
GRPCKeepAliveInterval: DefaultGRPCKeepAliveInterval,
@@ -704,23 +566,14 @@ func NewConfig() *Config {
LogRotationConfigJSON: DefaultLogRotationConfig,
EnableGRPCGateway: true,
- ExperimentalDowngradeCheckTime: DefaultDowngradeCheckTime,
- DowngradeCheckTime: DefaultDowngradeCheckTime,
- MemoryMlock: false,
- // TODO: delete in v3.7
- ExperimentalMemoryMlock: false,
- ExperimentalStopGRPCServiceOnDefrag: false,
- MaxLearners: membership.DefaultMaxLearners,
+ DowngradeCheckTime: DefaultDowngradeCheckTime,
+ MemoryMlock: false,
+ MaxLearners: membership.DefaultMaxLearners,
- ExperimentalTxnModeWriteWithSharedBuffer: DefaultExperimentalTxnModeWriteWithSharedBuffer,
- ExperimentalDistributedTracingAddress: DefaultDistributedTracingAddress,
- DistributedTracingAddress: DefaultDistributedTracingAddress,
- ExperimentalDistributedTracingServiceName: DefaultDistributedTracingServiceName,
- DistributedTracingServiceName: DefaultDistributedTracingServiceName,
+ DistributedTracingAddress: DefaultDistributedTracingAddress,
+ DistributedTracingServiceName: DefaultDistributedTracingServiceName,
CompactHashCheckTime: DefaultCompactHashCheckTime,
- // TODO: delete in v3.7
- ExperimentalCompactHashCheckTime: DefaultCompactHashCheckTime,
V2Deprecation: config.V2DeprDefault,
@@ -807,8 +660,6 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
"List of this member's client URLs to advertise to the public.",
)
- fs.StringVar(&cfg.Durl, "discovery", cfg.Durl, "Discovery URL used to bootstrap the cluster for v2 discovery. Will be deprecated in v3.7, and be decommissioned in v3.8.")
-
fs.Var(
flags.NewUniqueStringsValue(""),
"discovery-endpoints",
@@ -827,7 +678,6 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
fs.StringVar(&cfg.DiscoveryCfg.Auth.Username, "discovery-user", "", "V3 discovery: username[:password] for authentication (prompt if password is not supplied).")
fs.StringVar(&cfg.DiscoveryCfg.Auth.Password, "discovery-password", "", "V3 discovery: password for authentication (if this option is used, --user option shouldn't include password).")
- fs.StringVar(&cfg.Dproxy, "discovery-proxy", cfg.Dproxy, "HTTP proxy to use for traffic to discovery service. Will be deprecated in v3.7, and be decommissioned in v3.8.")
fs.StringVar(&cfg.DNSCluster, "discovery-srv", cfg.DNSCluster, "DNS domain used to bootstrap initial cluster.")
fs.StringVar(&cfg.DNSClusterServiceName, "discovery-srv-name", cfg.DNSClusterServiceName, "Service name to query when using DNS discovery.")
fs.StringVar(&cfg.InitialCluster, "initial-cluster", cfg.InitialCluster, "Initial cluster configuration for bootstrapping.")
@@ -858,7 +708,6 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
fs.Var(flags.NewStringsValue(""), "peer-cert-allowed-cn", "Comma-separated list of allowed CNs for inter-peer TLS authentication.")
fs.Var(flags.NewStringsValue(""), "peer-cert-allowed-hostname", "Comma-separated list of allowed SAN hostnames for inter-peer TLS authentication.")
fs.Var(flags.NewStringsValue(""), "cipher-suites", "Comma-separated list of supported TLS cipher suites between client/server and peers (empty will be auto-populated by Go).")
- fs.BoolVar(&cfg.ExperimentalPeerSkipClientSanVerification, "experimental-peer-skip-client-san-verification", false, "Skip verification of SAN field in client certificate for peer connections.Deprecated in v3.6 and will be decommissioned in v3.7. Use peer-skip-client-san-verification instead")
fs.BoolVar(&cfg.PeerTLSInfo.SkipClientSANVerify, "peer-skip-client-san-verification", false, "Skip verification of SAN field in client certificate for peer connections.")
fs.StringVar(&cfg.TlsMinVersion, "tls-min-version", string(tlsutil.TLSVersion12), "Minimum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3.")
fs.StringVar(&cfg.TlsMaxVersion, "tls-max-version", string(tlsutil.TLSVersionDefault), "Maximum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3 (empty defers to Go).")
@@ -887,20 +736,10 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
// additional metrics
fs.StringVar(&cfg.Metrics, "metrics", cfg.Metrics, "Set level of detail for exported metrics, specify 'extensive' to include server side grpc histogram metrics")
- // experimental distributed tracing
- fs.BoolVar(&cfg.ExperimentalEnableDistributedTracing, "experimental-enable-distributed-tracing", false, "Enable experimental distributed tracing using OpenTelemetry Tracing. Deprecated in v3.6 and will be decommissioned in v3.7. Use --enable-distributed-tracing instead.")
fs.BoolVar(&cfg.EnableDistributedTracing, "enable-distributed-tracing", false, "Enable distributed tracing using OpenTelemetry Tracing.")
-
- fs.StringVar(&cfg.ExperimentalDistributedTracingAddress, "experimental-distributed-tracing-address", cfg.ExperimentalDistributedTracingAddress, "Address for distributed tracing used for OpenTelemetry Tracing (if enabled with experimental-enable-distributed-tracing flag). Deprecated in v3.6 and will be decommissioned in v3.7. Use --distributed-tracing-address instead.")
fs.StringVar(&cfg.DistributedTracingAddress, "distributed-tracing-address", cfg.DistributedTracingAddress, "Address for distributed tracing used for OpenTelemetry Tracing (if enabled with enable-distributed-tracing flag).")
-
- fs.StringVar(&cfg.ExperimentalDistributedTracingServiceName, "experimental-distributed-tracing-service-name", cfg.ExperimentalDistributedTracingServiceName, "Configures service name for distributed tracing to be used to define service name for OpenTelemetry Tracing (if enabled with experimental-enable-distributed-tracing flag). 'etcd' is the default service name. Use the same service name for all instances of etcd. Deprecated in v3.6 and will be decommissioned in v3.7. Use --distributed-tracing-service-name instead.")
fs.StringVar(&cfg.DistributedTracingServiceName, "distributed-tracing-service-name", cfg.DistributedTracingServiceName, "Configures service name for distributed tracing to be used to define service name for OpenTelemetry Tracing (if enabled with enable-distributed-tracing flag). 'etcd' is the default service name. Use the same service name for all instances of etcd.")
-
- fs.StringVar(&cfg.ExperimentalDistributedTracingServiceInstanceID, "experimental-distributed-tracing-instance-id", "", "Configures service instance ID for distributed tracing to be used to define service instance ID key for OpenTelemetry Tracing (if enabled with experimental-enable-distributed-tracing flag). There is no default value set. This ID must be unique per etcd instance. Deprecated in v3.6 and will be decommissioned in v3.7. Use --distributed-tracing-instance-id instead.")
fs.StringVar(&cfg.DistributedTracingServiceInstanceID, "distributed-tracing-instance-id", "", "Configures service instance ID for distributed tracing to be used to define service instance ID key for OpenTelemetry Tracing (if enabled with enable-distributed-tracing flag). There is no default value set. This ID must be unique per etcd instance.")
-
- fs.IntVar(&cfg.ExperimentalDistributedTracingSamplingRatePerMillion, "experimental-distributed-tracing-sampling-rate", 0, "Number of samples to collect per million spans for OpenTelemetry Tracing (if enabled with experimental-enable-distributed-tracing flag). Deprecated in v3.6 and will be decommissioned in v3.7. Use --distributed-tracing-sampling-rate instead.")
fs.IntVar(&cfg.DistributedTracingSamplingRatePerMillion, "distributed-tracing-sampling-rate", 0, "Number of samples to collect per million spans for OpenTelemetry Tracing (if enabled with enable-distributed-tracing flag).")
// auth
@@ -910,48 +749,18 @@ func (cfg *Config) AddFlags(fs *flag.FlagSet) {
// gateway
fs.BoolVar(&cfg.EnableGRPCGateway, "enable-grpc-gateway", cfg.EnableGRPCGateway, "Enable GRPC gateway.")
-
- // experimental
- fs.BoolVar(&cfg.ExperimentalInitialCorruptCheck, "experimental-initial-corrupt-check", cfg.ExperimentalInitialCorruptCheck, "Enable to check data corruption before serving any client/peer traffic.")
- // TODO: delete in v3.7
- fs.DurationVar(&cfg.ExperimentalCorruptCheckTime, "experimental-corrupt-check-time", cfg.ExperimentalCorruptCheckTime, "Duration of time between cluster corruption check passes. Deprecated in v3.6 and will be decommissioned in v3.7. Use --corrupt-check-time instead")
fs.DurationVar(&cfg.CorruptCheckTime, "corrupt-check-time", cfg.CorruptCheckTime, "Duration of time between cluster corruption check passes.")
- // TODO: delete in v3.7
- fs.BoolVar(&cfg.ExperimentalCompactHashCheckEnabled, "experimental-compact-hash-check-enabled", cfg.ExperimentalCompactHashCheckEnabled, "Enable leader to periodically check followers compaction hashes. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=CompactHashCheck=true' instead")
- fs.DurationVar(&cfg.ExperimentalCompactHashCheckTime, "experimental-compact-hash-check-time", cfg.ExperimentalCompactHashCheckTime, "Duration of time between leader checks followers compaction hashes. Deprecated in v3.6 and will be decommissioned in v3.7. Use --compact-hash-check-time instead.")
-
fs.DurationVar(&cfg.CompactHashCheckTime, "compact-hash-check-time", cfg.CompactHashCheckTime, "Duration of time between leader checks followers compaction hashes.")
- fs.BoolVar(&cfg.ExperimentalEnableLeaseCheckpoint, "experimental-enable-lease-checkpoint", false, "Enable leader to send regular checkpoints to other members to prevent reset of remaining TTL on leader change.")
- // TODO: delete in v3.7
- fs.BoolVar(&cfg.ExperimentalEnableLeaseCheckpointPersist, "experimental-enable-lease-checkpoint-persist", false, "Enable persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled. Requires experimental-enable-lease-checkpoint to be enabled.")
- // TODO: delete in v3.7
- fs.IntVar(&cfg.ExperimentalCompactionBatchLimit, "experimental-compaction-batch-limit", cfg.ExperimentalCompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch. Deprecated in v3.6 and will be decommissioned in v3.7. Use --compaction-batch-limit instead.")
fs.IntVar(&cfg.CompactionBatchLimit, "compaction-batch-limit", cfg.CompactionBatchLimit, "Sets the maximum revisions deleted in each compaction batch.")
- fs.DurationVar(&cfg.ExperimentalCompactionSleepInterval, "experimental-compaction-sleep-interval", cfg.ExperimentalCompactionSleepInterval, "Sets the sleep interval between each compaction batch. Deprecated in v3.6 and will be decommissioned in v3.7. Use --compaction-sleep-interval instead.")
fs.DurationVar(&cfg.CompactionSleepInterval, "compaction-sleep-interval", cfg.CompactionSleepInterval, "Sets the sleep interval between each compaction batch.")
- // TODO: delete in v3.7
- fs.DurationVar(&cfg.ExperimentalWatchProgressNotifyInterval, "experimental-watch-progress-notify-interval", cfg.ExperimentalWatchProgressNotifyInterval, "Duration of periodic watch progress notifications. Deprecated in v3.6 and will be decommissioned in v3.7. Use --watch-progress-notify-interval instead.")
fs.DurationVar(&cfg.WatchProgressNotifyInterval, "watch-progress-notify-interval", cfg.WatchProgressNotifyInterval, "Duration of periodic watch progress notifications.")
fs.DurationVar(&cfg.DowngradeCheckTime, "downgrade-check-time", cfg.DowngradeCheckTime, "Duration of time between two downgrade status checks.")
- // TODO: delete in v3.7
- fs.DurationVar(&cfg.ExperimentalDowngradeCheckTime, "experimental-downgrade-check-time", cfg.ExperimentalDowngradeCheckTime, "Duration of time between two downgrade status checks. Deprecated in v3.6 and will be decommissioned in v3.7. Use --downgrade-check-time instead.")
- // TODO: delete in v3.7
- fs.DurationVar(&cfg.ExperimentalWarningApplyDuration, "experimental-warning-apply-duration", cfg.ExperimentalWarningApplyDuration, "Time duration after which a warning is generated if request takes more time. Deprecated in v3.6 and will be decommissioned in v3.7. Use --warning-watch-progress-duration instead.")
fs.DurationVar(&cfg.WarningApplyDuration, "warning-apply-duration", cfg.WarningApplyDuration, "Time duration after which a warning is generated if watch progress takes more time.")
fs.DurationVar(&cfg.WarningUnaryRequestDuration, "warning-unary-request-duration", cfg.WarningUnaryRequestDuration, "Time duration after which a warning is generated if a unary request takes more time.")
- fs.DurationVar(&cfg.ExperimentalWarningUnaryRequestDuration, "experimental-warning-unary-request-duration", cfg.ExperimentalWarningUnaryRequestDuration, "Time duration after which a warning is generated if a unary request takes more time. It's deprecated, and will be decommissioned in v3.7. Use --warning-unary-request-duration instead.")
- // TODO: delete in v3.7
- fs.BoolVar(&cfg.ExperimentalMemoryMlock, "experimental-memory-mlock", cfg.ExperimentalMemoryMlock, "Enable to enforce etcd pages (in particular bbolt) to stay in RAM.")
fs.BoolVar(&cfg.MemoryMlock, "memory-mlock", cfg.MemoryMlock, "Enable to enforce etcd pages (in particular bbolt) to stay in RAM.")
- fs.BoolVar(&cfg.ExperimentalTxnModeWriteWithSharedBuffer, "experimental-txn-mode-write-with-shared-buffer", true, "Enable the write transaction to use a shared buffer in its readonly check operations.")
- fs.BoolVar(&cfg.ExperimentalStopGRPCServiceOnDefrag, "experimental-stop-grpc-service-on-defrag", cfg.ExperimentalStopGRPCServiceOnDefrag, "Enable etcd gRPC service to stop serving client requests on defragmentation.")
- // TODO: delete in v3.7
- fs.UintVar(&cfg.ExperimentalBootstrapDefragThresholdMegabytes, "experimental-bootstrap-defrag-threshold-megabytes", 0, "Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect. It's deprecated, and will be decommissioned in v3.7. Use --bootstrap-defrag-threshold-megabytes instead.")
fs.UintVar(&cfg.BootstrapDefragThresholdMegabytes, "bootstrap-defrag-threshold-megabytes", 0, "Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.")
- // TODO: delete in v3.7
fs.IntVar(&cfg.MaxLearners, "max-learners", membership.DefaultMaxLearners, "Sets the maximum number of learners that can be available in the cluster membership.")
- fs.Uint64Var(&cfg.ExperimentalSnapshotCatchUpEntries, "experimental-snapshot-catchup-entries", cfg.ExperimentalSnapshotCatchUpEntries, "Number of entries for a slow follower to catch up after compacting the raft storage entries. Deprecated in v3.6 and will be decommissioned in v3.7. Use --snapshot-catchup-entries instead.")
fs.Uint64Var(&cfg.SnapshotCatchUpEntries, "snapshot-catchup-entries", cfg.SnapshotCatchUpEntries, "Number of entries for a slow follower to catch up after compacting the raft storage entries.")
// unsafe
@@ -1011,28 +820,6 @@ func (cfg *configYAML) configFromFile(path string) error {
}
}
- // attempt to fix a bug introduced in https://github.com/etcd-io/etcd/pull/15033
- // both `experimental-snapshot-catch-up-entries` and `experimental-snapshot-catchup-entries` refer to the same field,
- // map the YAML field "experimental-snapshot-catch-up-entries" to the flag "experimental-snapshot-catchup-entries".
- if val, ok := cfgMap["experimental-snapshot-catch-up-entries"]; ok {
- cfgMap["experimental-snapshot-catchup-entries"] = val
- cfg.ExperimentalSnapshotCatchUpEntries = uint64(val.(float64))
- cfg.FlagsExplicitlySet["experimental-snapshot-catchup-entries"] = true
- }
-
- getBoolFlagVal := func(flagName string) *bool {
- flagVal, ok := cfgMap[flagName]
- if !ok {
- return nil
- }
- boolVal := flagVal.(bool)
- return &boolVal
- }
- err = SetFeatureGatesFromExperimentalFlags(cfg.ServerFeatureGate, getBoolFlagVal, cfg.configJSON.ServerFeatureGatesJSON)
- if err != nil {
- return err
- }
-
if cfg.configJSON.ListenPeerURLs != "" {
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenPeerURLs, ","))
if err != nil {
@@ -1098,7 +885,7 @@ func (cfg *configYAML) configFromFile(path string) error {
}
// If a discovery or discovery-endpoints flag is set, clear default initial cluster set by InitialClusterFromName
- if (cfg.Durl != "" || cfg.DNSCluster != "" || len(cfg.DiscoveryCfg.Endpoints) > 0) && cfg.InitialCluster == defaultInitialCluster {
+ if (cfg.DNSCluster != "" || len(cfg.DiscoveryCfg.Endpoints) > 0) && cfg.InitialCluster == defaultInitialCluster {
cfg.InitialCluster = ""
}
if cfg.ClusterState == "" {
@@ -1126,36 +913,6 @@ func (cfg *configYAML) configFromFile(path string) error {
return cfg.Validate()
}
-// SetFeatureGatesFromExperimentalFlags sets the feature gate values if the feature gate is not explicitly set
-// while their corresponding experimental flags are explicitly set, for all the features in ExperimentalFlagToFeatureMap.
-// TODO: remove after all experimental flags are deprecated.
-func SetFeatureGatesFromExperimentalFlags(fg featuregate.FeatureGate, getExperimentalFlagVal func(string) *bool, featureGatesVal string) error {
- m := make(map[featuregate.Feature]bool)
- // verify that the feature gate and its experimental flag are not both set at the same time.
- for expFlagName, featureName := range features.ExperimentalFlagToFeatureMap {
- flagVal := getExperimentalFlagVal(expFlagName)
- if flagVal == nil {
- continue
- }
- if strings.Contains(featureGatesVal, string(featureName)) {
- return fmt.Errorf("cannot specify both flags: --%s=%v and --%s=%s=%v at the same time, please just use --%s=%s=%v",
- expFlagName, *flagVal, ServerFeatureGateFlagName, featureName, fg.Enabled(featureName), ServerFeatureGateFlagName, featureName, fg.Enabled(featureName))
- }
- m[featureName] = *flagVal
- }
-
- // filter out unknown features for fg, because we could use SetFeatureGatesFromExperimentalFlags both for
- // server and cluster level feature gates.
- allFeatures := fg.(featuregate.MutableFeatureGate).GetAll()
- mFiltered := make(map[string]bool)
- for k, v := range m {
- if _, ok := allFeatures[k]; ok {
- mFiltered[string(k)] = v
- }
- }
- return fg.(featuregate.MutableFeatureGate).SetFromMap(mFiltered)
-}
-
func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
if len(tls.CipherSuites) > 0 && len(ss) > 0 {
return fmt.Errorf("TLSInfo.CipherSuites is already specified (given %v)", ss)
@@ -1183,14 +940,6 @@ func updateMinMaxVersions(info *transport.TLSInfo, min, max string) {
// Validate ensures that '*embed.Config' fields are properly configured.
func (cfg *Config) Validate() error {
- // make sure there is no conflict in the flag settings in the ExperimentalNonBoolFlagMigrationMap
- // TODO: delete in v3.7
- for oldFlag, newFlag := range experimentalFlagMigrationMap {
- if cfg.FlagsExplicitlySet[oldFlag] && cfg.FlagsExplicitlySet[newFlag] {
- return fmt.Errorf("cannot set --%s and --%s at the same time, please use --%s only", oldFlag, newFlag, newFlag)
- }
- }
-
if err := cfg.setupLogging(); err != nil {
return err
}
@@ -1219,7 +968,7 @@ func (cfg *Config) Validate() error {
}
// Check if conflicting flags are passed.
nSet := 0
- for _, v := range []bool{cfg.Durl != "", cfg.InitialCluster != "", cfg.DNSCluster != "", len(cfg.DiscoveryCfg.Endpoints) > 0} {
+ for _, v := range []bool{cfg.InitialCluster != "", cfg.DNSCluster != "", len(cfg.DiscoveryCfg.Endpoints) > 0} {
if v {
nSet++
}
@@ -1233,22 +982,6 @@ func (cfg *Config) Validate() error {
return ErrConflictBootstrapFlags
}
- // Check if both v2 discovery and v3 discovery flags are passed.
- v2discoveryFlagsExist := cfg.Dproxy != ""
- v3discoveryFlagsExist := len(cfg.DiscoveryCfg.Endpoints) > 0 ||
- cfg.DiscoveryCfg.Token != "" ||
- cfg.DiscoveryCfg.Secure.Cert != "" ||
- cfg.DiscoveryCfg.Secure.Key != "" ||
- cfg.DiscoveryCfg.Secure.Cacert != "" ||
- cfg.DiscoveryCfg.Auth.Username != "" ||
- cfg.DiscoveryCfg.Auth.Password != ""
-
- if v2discoveryFlagsExist && v3discoveryFlagsExist {
- return errors.New("both v2 discovery settings (discovery, discovery-proxy) " +
- "and v3 discovery settings (discovery-token, discovery-endpoints, discovery-cert, " +
- "discovery-key, discovery-cacert, discovery-user, discovery-password) are set")
- }
-
// If one of `discovery-token` and `discovery-endpoints` is provided,
// then the other one must be provided as well.
if (cfg.DiscoveryCfg.Token != "") != (len(cfg.DiscoveryCfg.Endpoints) > 0) {
@@ -1301,10 +1034,7 @@ func (cfg *Config) Validate() error {
if cfg.ServerFeatureGate.Enabled(features.LeaseCheckpointPersist) && !cfg.ServerFeatureGate.Enabled(features.LeaseCheckpoint) {
return fmt.Errorf("enabling feature gate LeaseCheckpointPersist requires enabling feature gate LeaseCheckpoint")
}
- // TODO: delete in v3.7
- if cfg.ExperimentalCompactHashCheckTime <= 0 {
- return fmt.Errorf("--experimental-compact-hash-check-time must be >0 (set to %v)", cfg.ExperimentalCompactHashCheckTime)
- }
+
if cfg.CompactHashCheckTime <= 0 {
return fmt.Errorf("--compact-hash-check-time must be >0 (set to %v)", cfg.CompactHashCheckTime)
}
@@ -1347,13 +1077,6 @@ func (cfg *Config) Validate() error {
func (cfg *Config) PeerURLsMapAndToken(which string) (urlsmap types.URLsMap, token string, err error) {
token = cfg.InitialClusterToken
switch {
- case cfg.Durl != "":
- urlsmap = types.URLsMap{}
- // If using v2 discovery, generate a temporary cluster based on
- // self's advertised peer URLs
- urlsmap[cfg.Name] = cfg.AdvertisePeerUrls
- token = cfg.Durl
-
case len(cfg.DiscoveryCfg.Endpoints) > 0:
urlsmap = types.URLsMap{}
// If using v3 discovery, generate a temporary cluster based on
diff --git a/server/embed/config_test.go b/server/embed/config_test.go
index c40e0e63a474..7ae26d776f24 100644
--- a/server/embed/config_test.go
+++ b/server/embed/config_test.go
@@ -22,7 +22,6 @@ import (
"net"
"net/url"
"os"
- "strconv"
"testing"
"time"
@@ -99,16 +98,10 @@ func TestConfigFileOtherFields(t *testing.T) {
func TestConfigFileFeatureGates(t *testing.T) {
testCases := []struct {
- name string
- serverFeatureGatesJSON string
- experimentalStopGRPCServiceOnDefrag string
- experimentalInitialCorruptCheck string
- experimentalCompactHashCheckEnabled string
- experimentalTxnModeWriteWithSharedBuffer string
- experimentalEnableLeaseCheckpoint string
- experimentalEnableLeaseCheckpointPersist string
- expectErr bool
- expectedFeatures map[featuregate.Feature]bool
+ name string
+ serverFeatureGatesJSON string
+ expectErr bool
+ expectedFeatures map[featuregate.Feature]bool
}{
{
name: "default",
@@ -120,94 +113,6 @@ func TestConfigFileFeatureGates(t *testing.T) {
features.LeaseCheckpointPersist: false,
},
},
- {
- name: "cannot set both experimental flag and feature gate flag for StopGRPCServiceOnDefrag",
- serverFeatureGatesJSON: "StopGRPCServiceOnDefrag=true",
- experimentalStopGRPCServiceOnDefrag: "false",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and feature gate flag for InitialCorruptCheck",
- serverFeatureGatesJSON: "InitialCorruptCheck=true",
- experimentalInitialCorruptCheck: "false",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and feature gate flag for TxnModeWriteWithSharedBuffer",
- serverFeatureGatesJSON: "TxnModeWriteWithSharedBuffer=true",
- experimentalTxnModeWriteWithSharedBuffer: "false",
- expectErr: true,
- },
- {
- name: "ok to set different experimental flag and feature gate flag",
- serverFeatureGatesJSON: "InitialCorruptCheck=true",
- experimentalStopGRPCServiceOnDefrag: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- features.TxnModeWriteWithSharedBuffer: true,
- features.InitialCorruptCheck: true,
- },
- },
- {
- name: "ok to set different multiple experimental flags and feature gate flags",
- serverFeatureGatesJSON: "StopGRPCServiceOnDefrag=true,TxnModeWriteWithSharedBuffer=true,LeaseCheckpoint=true,SetMemberLocalAddr=true",
- experimentalCompactHashCheckEnabled: "true",
- experimentalInitialCorruptCheck: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- features.CompactHashCheck: true,
- features.InitialCorruptCheck: true,
- features.TxnModeWriteWithSharedBuffer: true,
- features.LeaseCheckpoint: true,
- features.SetMemberLocalAddr: true,
- },
- },
- {
- name: "can set feature gate StopGRPCServiceOnDefrag to true from experimental flag",
- experimentalStopGRPCServiceOnDefrag: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate StopGRPCServiceOnDefrag to false from experimental flag",
- experimentalStopGRPCServiceOnDefrag: "false",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: false,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate experimentalInitialCorruptCheck to true from experimental flag",
- experimentalInitialCorruptCheck: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.InitialCorruptCheck: true,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate experimentalInitialCorruptCheck to false from experimental flag",
- experimentalInitialCorruptCheck: "false",
- expectedFeatures: map[featuregate.Feature]bool{
- features.InitialCorruptCheck: false,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate TxnModeWriteWithSharedBuffer to true from experimental flag",
- experimentalTxnModeWriteWithSharedBuffer: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate TxnModeWriteWithSharedBuffer to false from experimental flag",
- experimentalTxnModeWriteWithSharedBuffer: "false",
- expectedFeatures: map[featuregate.Feature]bool{
- features.TxnModeWriteWithSharedBuffer: false,
- },
- },
{
name: "can set feature gate StopGRPCServiceOnDefrag to true from feature gate flag",
serverFeatureGatesJSON: "StopGRPCServiceOnDefrag=true",
@@ -246,28 +151,6 @@ func TestConfigFileFeatureGates(t *testing.T) {
features.TxnModeWriteWithSharedBuffer: false,
},
},
- {
- name: "cannot set both experimental flag and feature gate flag for ExperimentalCompactHashCheckEnabled",
- serverFeatureGatesJSON: "CompactHashCheck=true",
- experimentalCompactHashCheckEnabled: "false",
- expectErr: true,
- },
- {
- name: "can set feature gate experimentalCompactHashCheckEnabled to true from experimental flag",
- experimentalCompactHashCheckEnabled: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.CompactHashCheck: true,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
- {
- name: "can set feature gate experimentalCompactHashCheckEnabled to false from experimental flag",
- experimentalCompactHashCheckEnabled: "false",
- expectedFeatures: map[featuregate.Feature]bool{
- features.CompactHashCheck: false,
- features.TxnModeWriteWithSharedBuffer: true,
- },
- },
{
name: "can set feature gate CompactHashCheck to true from feature gate flag",
serverFeatureGatesJSON: "CompactHashCheck=true",
@@ -276,17 +159,6 @@ func TestConfigFileFeatureGates(t *testing.T) {
features.TxnModeWriteWithSharedBuffer: true,
},
},
- {
- name: "can set feature gate experimentalEnableLeaseCheckpoint and experimentalEnableLeaseCheckpointPersist to true from experimental flag",
- experimentalEnableLeaseCheckpoint: "true",
- experimentalEnableLeaseCheckpointPersist: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.CompactHashCheck: false,
- features.TxnModeWriteWithSharedBuffer: true,
- features.LeaseCheckpoint: true,
- features.LeaseCheckpointPersist: true,
- },
- },
{
name: "can set feature gate LeaseCheckpoint and LeaseCheckpointPersist to true from feature gate flag",
serverFeatureGatesJSON: "LeaseCheckpointPersist=true,LeaseCheckpoint=true",
@@ -296,75 +168,15 @@ func TestConfigFileFeatureGates(t *testing.T) {
features.LeaseCheckpointPersist: true,
},
},
- {
- name: "cannot set feature gate experimentalEnableLeaseCheckpoint=false and experimentalEnableLeaseCheckpointPersist=true",
- experimentalEnableLeaseCheckpoint: "false",
- experimentalEnableLeaseCheckpointPersist: "true",
- expectErr: true,
- },
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
yc := struct {
- ExperimentalStopGRPCServiceOnDefrag *bool `json:"experimental-stop-grpc-service-on-defrag,omitempty"`
- ExperimentalInitialCorruptCheck *bool `json:"experimental-initial-corrupt-check,omitempty"`
- ExperimentalCompactHashCheckEnabled *bool `json:"experimental-compact-hash-check-enabled,omitempty"`
- ExperimentalTxnModeWriteWithSharedBuffer *bool `json:"experimental-txn-mode-write-with-shared-buffer,omitempty"`
- ExperimentalEnableLeaseCheckpoint *bool `json:"experimental-enable-lease-checkpoint,omitempty"`
- ExperimentalEnableLeaseCheckpointPersist *bool `json:"experimental-enable-lease-checkpoint-persist,omitempty"`
- ServerFeatureGatesJSON string `json:"feature-gates"`
+ ServerFeatureGatesJSON string `json:"feature-gates"`
}{
ServerFeatureGatesJSON: tc.serverFeatureGatesJSON,
}
- if tc.experimentalInitialCorruptCheck != "" {
- experimentalInitialCorruptCheck, err := strconv.ParseBool(tc.experimentalInitialCorruptCheck)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalInitialCorruptCheck = &experimentalInitialCorruptCheck
- }
-
- if tc.experimentalTxnModeWriteWithSharedBuffer != "" {
- experimentalTxnModeWriteWithSharedBuffer, err := strconv.ParseBool(tc.experimentalTxnModeWriteWithSharedBuffer)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalTxnModeWriteWithSharedBuffer = &experimentalTxnModeWriteWithSharedBuffer
- }
-
- if tc.experimentalStopGRPCServiceOnDefrag != "" {
- experimentalStopGRPCServiceOnDefrag, err := strconv.ParseBool(tc.experimentalStopGRPCServiceOnDefrag)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalStopGRPCServiceOnDefrag = &experimentalStopGRPCServiceOnDefrag
- }
-
- if tc.experimentalCompactHashCheckEnabled != "" {
- experimentalCompactHashCheckEnabled, err := strconv.ParseBool(tc.experimentalCompactHashCheckEnabled)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalCompactHashCheckEnabled = &experimentalCompactHashCheckEnabled
- }
-
- if tc.experimentalEnableLeaseCheckpoint != "" {
- experimentalEnableLeaseCheckpoint, err := strconv.ParseBool(tc.experimentalEnableLeaseCheckpoint)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalEnableLeaseCheckpoint = &experimentalEnableLeaseCheckpoint
- }
-
- if tc.experimentalEnableLeaseCheckpointPersist != "" {
- experimentalEnableLeaseCheckpointPersist, err := strconv.ParseBool(tc.experimentalEnableLeaseCheckpointPersist)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalEnableLeaseCheckpointPersist = &experimentalEnableLeaseCheckpointPersist
- }
-
b, err := yaml.Marshal(&yc)
if err != nil {
t.Fatal(err)
@@ -446,111 +258,111 @@ func TestInferLocalAddr(t *testing.T) {
expectedLocalAddr string
}{
{
- "defaults, ExperimentalSetMemberLocalAddr=false ",
+ "defaults, SetMemberLocalAddr=false ",
[]string{DefaultInitialAdvertisePeerURLs},
"SetMemberLocalAddr=false",
"",
},
{
- "IPv4 address, ExperimentalSetMemberLocalAddr=false ",
+ "IPv4 address, SetMemberLocalAddr=false ",
[]string{"https://192.168.100.110:2380"},
"SetMemberLocalAddr=false",
"",
},
{
- "defaults, ExperimentalSetMemberLocalAddr=true",
+ "defaults, SetMemberLocalAddr=true",
[]string{DefaultInitialAdvertisePeerURLs},
"SetMemberLocalAddr=true",
"",
},
{
- "IPv4 unspecified address, ExperimentalSetMemberLocalAddr=true",
+ "IPv4 unspecified address, SetMemberLocalAddr=true",
[]string{"https://0.0.0.0:2380"},
"SetMemberLocalAddr=true",
"",
},
{
- "IPv6 unspecified address, ExperimentalSetMemberLocalAddr=true",
+ "IPv6 unspecified address, SetMemberLocalAddr=true",
[]string{"https://[::]:2380"},
"SetMemberLocalAddr=true",
"",
},
{
- "IPv4 loopback address, ExperimentalSetMemberLocalAddr=true",
+ "IPv4 loopback address, SetMemberLocalAddr=true",
[]string{"https://127.0.0.1:2380"},
"SetMemberLocalAddr=true",
"",
},
{
- "IPv6 loopback address, ExperimentalSetMemberLocalAddr=true",
+ "IPv6 loopback address, SetMemberLocalAddr=true",
[]string{"https://[::1]:2380"},
"SetMemberLocalAddr=true",
"",
},
{
- "IPv4 address, ExperimentalSetMemberLocalAddr=true",
+ "IPv4 address, SetMemberLocalAddr=true",
[]string{"https://192.168.100.110:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "Hostname only, ExperimentalSetMemberLocalAddr=true",
+ "Hostname only, SetMemberLocalAddr=true",
[]string{"https://123-host-3.corp.internal:2380"},
"SetMemberLocalAddr=true",
"",
},
{
- "Hostname and IPv4 address, ExperimentalSetMemberLocalAddr=true",
+ "Hostname and IPv4 address, SetMemberLocalAddr=true",
[]string{"https://123-host-3.corp.internal:2380", "https://192.168.100.110:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "IPv4 address and Hostname, ExperimentalSetMemberLocalAddr=true",
+ "IPv4 address and Hostname, SetMemberLocalAddr=true",
[]string{"https://192.168.100.110:2380", "https://123-host-3.corp.internal:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "IPv4 and IPv6 addresses, ExperimentalSetMemberLocalAddr=true",
+ "IPv4 and IPv6 addresses, SetMemberLocalAddr=true",
[]string{"https://192.168.100.110:2380", "https://[2001:db8:85a3::8a2e:370:7334]:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "IPv6 and IPv4 addresses, ExperimentalSetMemberLocalAddr=true",
+ "IPv6 and IPv4 addresses, SetMemberLocalAddr=true",
// IPv4 addresses will always sort before IPv6 ones anyway
[]string{"https://[2001:db8:85a3::8a2e:370:7334]:2380", "https://192.168.100.110:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "Hostname, IPv4 and IPv6 addresses, ExperimentalSetMemberLocalAddr=true",
+ "Hostname, IPv4 and IPv6 addresses, SetMemberLocalAddr=true",
[]string{"https://123-host-3.corp.internal:2380", "https://192.168.100.110:2380", "https://[2001:db8:85a3::8a2e:370:7334]:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "Hostname, IPv6 and IPv4 addresses, ExperimentalSetMemberLocalAddr=true",
+ "Hostname, IPv6 and IPv4 addresses, SetMemberLocalAddr=true",
// IPv4 addresses will always sort before IPv6 ones anyway
[]string{"https://123-host-3.corp.internal:2380", "https://[2001:db8:85a3::8a2e:370:7334]:2380", "https://192.168.100.110:2380"},
"SetMemberLocalAddr=true",
"192.168.100.110",
},
{
- "IPv6 address, ExperimentalSetMemberLocalAddr=true",
+ "IPv6 address, SetMemberLocalAddr=true",
[]string{"https://[2001:db8:85a3::8a2e:370:7334]:2380"},
"SetMemberLocalAddr=true",
"2001:db8:85a3::8a2e:370:7334",
},
{
- "Hostname and IPv6 address, ExperimentalSetMemberLocalAddr=true",
+ "Hostname and IPv6 address, SetMemberLocalAddr=true",
[]string{"https://123-host-3.corp.internal:2380", "https://[2001:db8:85a3::8a2e:370:7334]:2380"},
"SetMemberLocalAddr=true",
"2001:db8:85a3::8a2e:370:7334",
},
{
- "IPv6 address and Hostname, ExperimentalSetMemberLocalAddr=true",
+ "IPv6 address and Hostname, SetMemberLocalAddr=true",
[]string{"https://[2001:db8:85a3::8a2e:370:7334]:2380", "https://123-host-3.corp.internal:2380"},
"SetMemberLocalAddr=true",
"2001:db8:85a3::8a2e:370:7334",
@@ -616,7 +428,7 @@ func compareSlices(slice1, slice2 []string) bool {
}
func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
- tmpfile, err := os.CreateTemp("", "servercfg")
+ tmpfile, err := os.CreateTemp(t.TempDir(), "servercfg")
if err != nil {
t.Fatal(err)
}
@@ -962,117 +774,67 @@ func TestUndefinedAutoCompactionModeValidate(t *testing.T) {
require.Error(t, err)
}
-func TestSetFeatureGatesFromExperimentalFlags(t *testing.T) {
+func TestMatchNewConfigAddFlags(t *testing.T) {
+ cfg := NewConfig()
+ fs := flag.NewFlagSet("etcd", flag.ContinueOnError)
+ cfg.AddFlags(fs)
+ require.NoError(t, fs.Parse(nil))
+ // TODO: Reduce number of unexported fields set in config
+ if diff := cmp.Diff(NewConfig(), cfg, cmpopts.IgnoreUnexported(transport.TLSInfo{}, Config{}), cmp.Comparer(func(a, b featuregate.FeatureGate) bool {
+ return a.String() == b.String()
+ })); diff != "" {
+ t.Errorf("Diff: %s", diff)
+ }
+}
+
+func TestDiscoveryCfg(t *testing.T) {
testCases := []struct {
- name string
- featureGatesFlag string
- experimentalStopGRPCServiceOnDefrag string
- expectErr bool
- expectedFeatures map[featuregate.Feature]bool
+ name string
+ discoveryCfg v3discovery.DiscoveryConfig
+ wantErr bool
}{
{
- name: "default",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: false,
- "TestAlpha": false,
- "TestBeta": true,
+ name: "Valid discovery config",
+ discoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: []string{"http://10.0.0.100:2379", "http://10.0.0.101:2379"},
+ },
},
+ wantErr: false,
},
{
- name: "cannot set experimental flag and feature gate to true at the same time",
- featureGatesFlag: "StopGRPCServiceOnDefrag=true",
- experimentalStopGRPCServiceOnDefrag: "true",
- expectErr: true,
- },
- {
- name: "cannot set experimental flag and feature gate to false at the same time",
- featureGatesFlag: "StopGRPCServiceOnDefrag=false",
- experimentalStopGRPCServiceOnDefrag: "false",
- expectErr: true,
- },
- {
- name: "cannot set experimental flag and feature gate to different values at the same time",
- featureGatesFlag: "StopGRPCServiceOnDefrag=true",
- experimentalStopGRPCServiceOnDefrag: "false",
- expectErr: true,
- },
- {
- name: "can set experimental flag and other feature gates",
- featureGatesFlag: "TestAlpha=true,TestBeta=false",
- experimentalStopGRPCServiceOnDefrag: "true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- "TestAlpha": true,
- "TestBeta": false,
+ name: "Partial empty discovery endpoints",
+ discoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: []string{"http://10.0.0.100:2379", ""},
+ },
},
+ wantErr: true,
},
{
- name: "can set feature gate when its experimental flag is not explicitly set",
- featureGatesFlag: "TestAlpha=true,StopGRPCServiceOnDefrag=true",
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- "TestAlpha": true,
- "TestBeta": true,
+ name: "Empty discovery endpoint",
+ discoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{
+ Endpoints: []string{"", ""},
+ },
},
+ wantErr: true,
},
}
+
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- fg := features.NewDefaultServerFeatureGate("test", nil)
- err := fg.(featuregate.MutableFeatureGate).Add(
- map[featuregate.Feature]featuregate.FeatureSpec{
- "TestAlpha": {Default: false, PreRelease: featuregate.Alpha},
- "TestBeta": {Default: true, PreRelease: featuregate.Beta},
- })
- require.NoError(t, err)
+ cfg := NewConfig()
+ cfg.InitialCluster = ""
+ cfg.DiscoveryCfg = tc.discoveryCfg
+ cfg.DiscoveryCfg.Token = "foo"
+ err := cfg.Validate()
- fg.(featuregate.MutableFeatureGate).Set(tc.featureGatesFlag)
- var getExperimentalFlagVal func(flagName string) *bool
- if tc.experimentalStopGRPCServiceOnDefrag == "" {
- // experimental flag is not explicitly set
- getExperimentalFlagVal = func(flagName string) *bool {
- return nil
- }
- } else {
- // mexperimental flag is explicitly set
- getExperimentalFlagVal = func(flagName string) *bool {
- // only the experimental-stop-grpc-service-on-defrag flag can be set in this test.
- if flagName != "experimental-stop-grpc-service-on-defrag" {
- return nil
- }
- flagVal, parseErr := strconv.ParseBool(tc.experimentalStopGRPCServiceOnDefrag)
- require.NoError(t, parseErr)
- return &flagVal
- }
- }
- err = SetFeatureGatesFromExperimentalFlags(fg, getExperimentalFlagVal, tc.featureGatesFlag)
- if tc.expectErr {
- require.Error(t, err)
- return
- }
- require.NoError(t, err)
- for k, v := range tc.expectedFeatures {
- if fg.Enabled(k) != v {
- t.Errorf("expected feature gate %s=%v, got %v", k, v, fg.Enabled(k))
- }
- }
+ require.Equal(t, tc.wantErr, err != nil)
})
}
}
-func TestMatchNewConfigAddFlags(t *testing.T) {
- cfg := NewConfig()
- fs := flag.NewFlagSet("etcd", flag.ContinueOnError)
- cfg.AddFlags(fs)
- require.NoError(t, fs.Parse(nil))
- // TODO: Reduce number of unexported fields set in config
- if diff := cmp.Diff(NewConfig(), cfg, cmpopts.IgnoreUnexported(transport.TLSInfo{}, Config{}), cmp.Comparer(func(a, b featuregate.FeatureGate) bool {
- return a.String() == b.String()
- })); diff != "" {
- t.Errorf("Diff: %s", diff)
- }
-}
-
func TestDiscoveryCfg(t *testing.T) {
testCases := []struct {
name string
diff --git a/server/embed/config_tracing.go b/server/embed/config_tracing.go
index 0ca90fdc52af..e53541c28753 100644
--- a/server/embed/config_tracing.go
+++ b/server/embed/config_tracing.go
@@ -23,8 +23,10 @@ import (
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
- semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
+ semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
"go.uber.org/zap"
+
+ "go.etcd.io/etcd/pkg/v3/traceutil"
)
const maxSamplingRatePerMillion = 1000000
@@ -81,6 +83,8 @@ func newTracingExporter(ctx context.Context, cfg *Config) (*tracingExporter, err
),
)
+ traceutil.Init(traceProvider)
+
options := []otelgrpc.Option{
otelgrpc.WithPropagators(
propagation.NewCompositeTextMapPropagator(
diff --git a/server/embed/etcd.go b/server/embed/etcd.go
index 95c0d6d92f97..a9be763f881e 100644
--- a/server/embed/etcd.go
+++ b/server/embed/etcd.go
@@ -191,8 +191,6 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
MaxWALFiles: cfg.MaxWalFiles,
InitialPeerURLsMap: urlsmap,
InitialClusterToken: token,
- DiscoveryURL: cfg.Durl,
- DiscoveryProxy: cfg.Dproxy,
DiscoveryCfg: cfg.DiscoveryCfg,
NewCluster: cfg.IsNewCluster(),
PeerTLSInfo: cfg.PeerTLSInfo,
@@ -234,7 +232,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
BootstrapDefragThresholdMegabytes: cfg.BootstrapDefragThresholdMegabytes,
MaxLearners: cfg.MaxLearners,
V2Deprecation: cfg.V2DeprecationEffective(),
- ExperimentalLocalAddress: cfg.InferLocalAddr(),
+ LocalAddress: cfg.InferLocalAddr(),
ServerFeatureGate: cfg.ServerFeatureGate,
Metrics: cfg.Metrics,
}
@@ -255,7 +253,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
)
}
- srvcfg.PeerTLSInfo.LocalAddr = srvcfg.ExperimentalLocalAddress
+ srvcfg.PeerTLSInfo.LocalAddr = srvcfg.LocalAddress
print(e.cfg.logger, *cfg, srvcfg, memberInitialized)
@@ -348,7 +346,7 @@ func print(lg *zap.Logger, ec Config, sc config.ServerConfig, memberInitialized
zap.Strings("advertise-client-urls", ec.getAdvertiseClientURLs()),
zap.Strings("listen-client-urls", ec.getListenClientURLs()),
zap.Strings("listen-metrics-urls", ec.getMetricsURLs()),
- zap.String("experimental-local-address", sc.ExperimentalLocalAddress),
+ zap.String("local-address", sc.LocalAddress),
zap.Strings("cors", cors),
zap.Strings("host-whitelist", hss),
zap.String("initial-cluster", sc.InitialPeerURLsMap.String()),
@@ -366,8 +364,6 @@ func print(lg *zap.Logger, ec Config, sc config.ServerConfig, memberInitialized
zap.String("auto-compaction-mode", sc.AutoCompactionMode),
zap.Duration("auto-compaction-retention", sc.AutoCompactionRetention),
zap.String("auto-compaction-interval", sc.AutoCompactionRetention.String()),
- zap.String("discovery-url", sc.DiscoveryURL),
- zap.String("discovery-proxy", sc.DiscoveryProxy),
zap.String("discovery-token", sc.DiscoveryCfg.Token),
zap.String("discovery-endpoints", strings.Join(sc.DiscoveryCfg.Endpoints, ",")),
@@ -731,7 +727,7 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
}
sctx.l.Close()
cfg.logger.Warn(
- "closing peer listener",
+ "closing client listener",
zap.String("address", sctx.addr),
zap.Error(err),
)
@@ -835,13 +831,13 @@ func (e *Etcd) grpcGatewayDial(splitHTTP bool) (grpcDial func(ctx context.Contex
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
- return func(ctx context.Context) (*grpc.ClientConn, error) {
- conn, err := grpc.DialContext(ctx, addr, opts...)
+ return func(_ context.Context) (*grpc.ClientConn, error) {
+ conn, err := grpc.NewClient(addr, opts...)
if err != nil {
- sctx.lg.Error("grpc gateway failed to dial", zap.String("addr", addr), zap.Error(err))
+ sctx.lg.Error("failed to setup grpc-gateway client", zap.String("addr", addr), zap.Error(err))
return nil, err
}
- return conn, err
+ return conn, nil
}
}
diff --git a/server/embed/serve.go b/server/embed/serve.go
index e50529dfe438..2b28cf1cc6b9 100644
--- a/server/embed/serve.go
+++ b/server/embed/serve.go
@@ -233,6 +233,14 @@ func (sctx *serveCtx) serve(
return tlsErr
}
+ // In gRPC-only mode the gRPC stack owns the TLS handshake (via grpc.Creds).
+ // Wrapping sctx.l with a second TLS listener would cause a double-TLS failure,
+ // so inject CRL checking into the TLS config before the gRPC server is created
+ // (gRPC clones the config at creation time).
+ if onlyGRPC {
+ tlsinfo.ConfigureCRLVerification(tlscfg)
+ }
+
if grpcEnabled {
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
v3electionpb.RegisterElectionServer(gs, servElection)
diff --git a/server/embed/serve_test.go b/server/embed/serve_test.go
index 487a82c915cd..9d97d6e49581 100644
--- a/server/embed/serve_test.go
+++ b/server/embed/serve_test.go
@@ -15,9 +15,6 @@
package embed
import (
- "fmt"
- "net/url"
- "os"
"testing"
"github.com/stretchr/testify/require"
@@ -31,29 +28,12 @@ func TestStartEtcdWrongToken(t *testing.T) {
cfg := NewConfig()
- // Similar to function in integration/embed/embed_test.go for setting up Config.
- urls := newEmbedURLs(2)
- curls := []url.URL{urls[0]}
- purls := []url.URL{urls[1]}
- cfg.ListenClientUrls, cfg.AdvertiseClientUrls = curls, curls
- cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = purls, purls
- cfg.InitialCluster = ""
- for i := range purls {
- cfg.InitialCluster += ",default=" + purls[i].String()
- }
- cfg.InitialCluster = cfg.InitialCluster[1:]
+ testURLConfig := newConfigTestURLs()
+ applyTestURLConfig(cfg, testURLConfig)
+
cfg.Dir = tdir
cfg.AuthToken = "wrong-token"
_, err := StartEtcd(cfg)
require.ErrorIsf(t, err, auth.ErrInvalidAuthOpts, "expected %v, got %v", auth.ErrInvalidAuthOpts, err)
}
-
-func newEmbedURLs(n int) (urls []url.URL) {
- scheme := "unix"
- for i := 0; i < n; i++ {
- u, _ := url.Parse(fmt.Sprintf("%s://localhost:%d%06d", scheme, os.Getpid(), i))
- urls = append(urls, *u)
- }
- return urls
-}
diff --git a/server/embed/util_test.go b/server/embed/util_test.go
new file mode 100644
index 000000000000..e54ed09468b3
--- /dev/null
+++ b/server/embed/util_test.go
@@ -0,0 +1,58 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 embed
+
+import (
+ "fmt"
+ "net/url"
+ "os"
+)
+
+type ConfigTestURLs struct {
+ PeerURLs []url.URL
+ ClientURLs []url.URL
+ InitialCluster string
+}
+
+// Similar to function in integration/embed/embed_test.go for setting up Config.
+func newConfigTestURLs() *ConfigTestURLs {
+ urls := newEmbedURLs(2)
+ cfg := &ConfigTestURLs{
+ ClientURLs: []url.URL{urls[0]},
+ PeerURLs: []url.URL{urls[1]},
+ InitialCluster: "",
+ }
+ for i := range cfg.PeerURLs {
+ cfg.InitialCluster += ",default=" + cfg.PeerURLs[i].String()
+ }
+ cfg.InitialCluster = cfg.InitialCluster[1:]
+ return cfg
+}
+
+func newEmbedURLs(n int) (urls []url.URL) {
+ scheme := "unix"
+ for i := 0; i < n; i++ {
+ u, _ := url.Parse(fmt.Sprintf("%s://localhost:%d%06d", scheme, os.Getpid(), i))
+ urls = append(urls, *u)
+ }
+ return urls
+}
+
+// applyTestURLConfig applies test URL configuration to the provided Config.
+func applyTestURLConfig(cfg *Config, testURLConfig *ConfigTestURLs) {
+ cfg.ListenClientUrls, cfg.AdvertiseClientUrls = testURLConfig.ClientURLs, testURLConfig.ClientURLs
+ cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = testURLConfig.PeerURLs, testURLConfig.PeerURLs
+ cfg.InitialCluster = testURLConfig.InitialCluster
+}
diff --git a/server/etcdmain/config.go b/server/etcdmain/config.go
index 64d700c51fc8..e4e3d24c684b 100644
--- a/server/etcdmain/config.go
+++ b/server/etcdmain/config.go
@@ -60,23 +60,6 @@ var (
deprecatedFlags = map[string]string{
"max-snapshots": "--max-snapshots is deprecated in 3.6 and will be decommissioned in 3.8.",
"v2-deprecation": "--v2-deprecation is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.",
- "experimental-compact-hash-check-enabled": "--experimental-compact-hash-check-enabled is deprecated in 3.6 and will be decommissioned in 3.7. Use '--feature-gates=CompactHashCheck=true' instead.",
- "experimental-compact-hash-check-time": "--experimental-compact-hash-check-time is deprecated in 3.6 and will be decommissioned in 3.7. Use '--compact-hash-check-time' instead.",
- "experimental-txn-mode-write-with-shared-buffer": "--experimental-txn-mode-write-with-shared-buffer is deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=TxnModeWriteWithSharedBuffer=true' instead.",
- "experimental-corrupt-check-time": "--experimental-corrupt-check-time is deprecated in v3.6 and will be decommissioned in v3.7. Use '--corrupt-check-time' instead.",
- "experimental-compaction-batch-limit": "--experimental-compaction-batch-limit is deprecated in v3.6 and will be decommissioned in v3.7. Use '--compaction-batch-limit' instead.",
- "experimental-watch-progress-notify-interval": "--experimental-watch-progress-notify-interval is deprecated in v3.6 and will be decommissioned in v3.7. Use '--watch-progress-notify-interval' instead.",
- "experimental-warning-apply-duration": "--experimental-warning-apply-duration is deprecated in v3.6 and will be decommissioned in v3.7. Use '--warning-apply-duration' instead.",
- "experimental-bootstrap-defrag-threshold-megabytes": "--experimental-bootstrap-defrag-threshold-megabytes is deprecated in v3.6 and will be decommissioned in v3.7. Use '--bootstrap-defrag-threshold-megabytes' instead.",
- "experimental-memory-mlock": "--experimental-memory-mlock is deprecated in v3.6 and will be decommissioned in v3.7. Use '--memory-mlock' instead.",
- "experimental-snapshot-catchup-entries": "--experimental-snapshot-catchup-entries is deprecated in v3.6 and will be decommissioned in v3.7. Use '--snapshot-catchup-entries' instead.",
- "experimental-compaction-sleep-interval": "--experimental-compaction-sleep-interval is deprecated in v3.6 and will be decommissioned in v3.7. Use 'compaction-sleep-interval' instead.",
- "experimental-downgrade-check-time": "--experimental-downgrade-check-time is deprecated in v3.6 and will be decommissioned in v3.7. Use '--downgrade-check-time' instead.",
- "experimental-enable-distributed-tracing": "--experimental-enable-distributed-tracing is deprecated in 3.6 and will be decommissioned in 3.7. Use --enable-distributed-tracing instead.",
- "experimental-distributed-tracing-address": "--experimental-distributed-tracing-address is deprecated in 3.6 and will be decommissioned in 3.7. Use --distributed-tracing-address instead.",
- "experimental-distributed-tracing-service-name": "--experimental-distributed-tracing-service-name is deprecated in 3.6 and will be decommissioned in 3.7. Use --distributed-tracing-service-name instead.",
- "experimental-distributed-tracing-instance-id": "--experimental-distributed-tracing-instance-id is deprecated in 3.6 and will be decommissioned in 3.7. Use --distributed-tracing-instance-id instead.",
- "experimental-distributed-tracing-sampling-rate": "--experimental-distributed-tracing-sampling-rate is deprecated in 3.6 and will be decommissioned in 3.7. Use --distributed-tracing-sampling-rate instead.",
}
)
@@ -116,6 +99,7 @@ func newConfig() *config {
),
v2deprecation: flags.NewSelectiveStringsValue(
string(cconfig.V2Depr1WriteOnly),
+ string(cconfig.V2Depr1WriteOnlySkipCheck),
string(cconfig.V2Depr1WriteOnlyDrop),
string(cconfig.V2Depr2Gone)),
}
@@ -180,78 +164,10 @@ func (cfg *config) parse(arguments []string) error {
err = cfg.configFromCmdLine()
}
- // params related to experimental flag deprecation
- // TODO: delete in v3.7
- if cfg.ec.FlagsExplicitlySet["experimental-compact-hash-check-time"] {
- cfg.ec.CompactHashCheckTime = cfg.ec.ExperimentalCompactHashCheckTime
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-corrupt-check-time"] {
- cfg.ec.CorruptCheckTime = cfg.ec.ExperimentalCorruptCheckTime
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-compaction-batch-limit"] {
- cfg.ec.CompactionBatchLimit = cfg.ec.ExperimentalCompactionBatchLimit
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-watch-progress-notify-interval"] {
- cfg.ec.WatchProgressNotifyInterval = cfg.ec.ExperimentalWatchProgressNotifyInterval
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-warning-apply-duration"] {
- cfg.ec.WarningApplyDuration = cfg.ec.ExperimentalWarningApplyDuration
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-bootstrap-defrag-threshold-megabytes"] {
- cfg.ec.BootstrapDefragThresholdMegabytes = cfg.ec.ExperimentalBootstrapDefragThresholdMegabytes
- }
- if cfg.ec.FlagsExplicitlySet["experimental-peer-skip-client-san-verification"] {
- cfg.ec.PeerTLSInfo.SkipClientSANVerify = cfg.ec.ExperimentalPeerSkipClientSanVerification
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-memory-mlock"] {
- cfg.ec.MemoryMlock = cfg.ec.ExperimentalMemoryMlock
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-snapshot-catchup-entries"] {
- cfg.ec.SnapshotCatchUpEntries = cfg.ec.ExperimentalSnapshotCatchUpEntries
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-compaction-sleep-interval"] {
- cfg.ec.CompactionSleepInterval = cfg.ec.ExperimentalCompactionSleepInterval
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-downgrade-check-time"] {
- cfg.ec.DowngradeCheckTime = cfg.ec.ExperimentalDowngradeCheckTime
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-enable-distributed-tracing"] {
- cfg.ec.EnableDistributedTracing = cfg.ec.ExperimentalEnableDistributedTracing
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-distributed-tracing-address"] {
- cfg.ec.DistributedTracingAddress = cfg.ec.ExperimentalDistributedTracingAddress
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-distributed-tracing-service-name"] {
- cfg.ec.DistributedTracingServiceName = cfg.ec.ExperimentalDistributedTracingServiceName
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-distributed-tracing-instance-id"] {
- cfg.ec.DistributedTracingServiceInstanceID = cfg.ec.ExperimentalDistributedTracingServiceInstanceID
- }
-
- if cfg.ec.FlagsExplicitlySet["experimental-distributed-tracing-sampling-rate"] {
- cfg.ec.DistributedTracingSamplingRatePerMillion = cfg.ec.ExperimentalDistributedTracingSamplingRatePerMillion
- }
-
// `V2Deprecation` (--v2-deprecation) is deprecated and scheduled for removal in v3.8. The default value is enforced, ignoring user input.
cfg.ec.V2Deprecation = cconfig.V2DeprDefault
- cfg.ec.WarningUnaryRequestDuration, perr = cfg.parseWarningUnaryRequestDuration()
- if perr != nil {
- return perr
- }
+ cfg.ec.WarningUnaryRequestDuration = cfg.parseWarningUnaryRequestDuration()
// Check for deprecated options from both command line and config file
var warningsForDeprecatedOpts []string
@@ -338,7 +254,7 @@ func (cfg *config) configFromCmdLine() error {
}
// disable default initial-cluster if discovery is set
- if (cfg.ec.Durl != "" || cfg.ec.DNSCluster != "" || cfg.ec.DNSClusterServiceName != "" || len(cfg.ec.DiscoveryCfg.Endpoints) > 0) && !flags.IsSet(cfg.cf.flagSet, "initial-cluster") {
+ if (cfg.ec.DNSCluster != "" || cfg.ec.DNSClusterServiceName != "" || len(cfg.ec.DiscoveryCfg.Endpoints) > 0) && !flags.IsSet(cfg.cf.flagSet, "initial-cluster") {
cfg.ec.InitialCluster = ""
}
@@ -346,21 +262,6 @@ func (cfg *config) configFromCmdLine() error {
cfg.ec.FlagsExplicitlySet[f.Name] = true
})
- getBoolFlagVal := func(flagName string) *bool {
- boolVal, parseErr := flags.GetBoolFlagVal(cfg.cf.flagSet, flagName)
- if parseErr != nil {
- panic(parseErr)
- }
- return boolVal
- }
-
- // SetFeatureGatesFromExperimentalFlags validates that cmd line flags for experimental feature and their feature gates are not explicitly set simultaneously,
- // and passes the values of cmd line flags for experimental feature to the server feature gate.
- err = embed.SetFeatureGatesFromExperimentalFlags(cfg.ec.ServerFeatureGate, getBoolFlagVal, cfg.cf.flagSet.Lookup(embed.ServerFeatureGateFlagName).Value.String())
- if err != nil {
- return err
- }
-
return cfg.validate()
}
@@ -381,23 +282,10 @@ func (cfg *config) validate() error {
return cfg.ec.Validate()
}
-func (cfg *config) parseWarningUnaryRequestDuration() (time.Duration, error) {
- if cfg.ec.ExperimentalWarningUnaryRequestDuration != 0 && cfg.ec.WarningUnaryRequestDuration != 0 {
- return 0, errors.New(
- "both --experimental-warning-unary-request-duration and --warning-unary-request-duration flags are set. " +
- "Use only --warning-unary-request-duration")
- }
-
+func (cfg *config) parseWarningUnaryRequestDuration() time.Duration {
if cfg.ec.WarningUnaryRequestDuration != 0 {
- return cfg.ec.WarningUnaryRequestDuration, nil
- }
-
- if cfg.ec.ExperimentalWarningUnaryRequestDuration != 0 {
- cfg.ec.GetLogger().Warn(
- "--experimental-warning-unary-request-duration is deprecated, and will be decommissioned in v3.7. " +
- "Use --warning-unary-request-duration instead.")
- return cfg.ec.ExperimentalWarningUnaryRequestDuration, nil
+ return cfg.ec.WarningUnaryRequestDuration
}
- return embed.DefaultWarningUnaryRequestDuration, nil
+ return embed.DefaultWarningUnaryRequestDuration
}
diff --git a/server/etcdmain/config_test.go b/server/etcdmain/config_test.go
index ae78786aac96..b12e25c4867c 100644
--- a/server/etcdmain/config_test.go
+++ b/server/etcdmain/config_test.go
@@ -21,19 +21,18 @@ import (
"net/url"
"os"
"reflect"
- "strconv"
"strings"
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"
+ clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/featuregate"
"go.etcd.io/etcd/pkg/v3/flags"
"go.etcd.io/etcd/server/v3/embed"
- "go.etcd.io/etcd/server/v3/etcdserver"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/v3discovery"
"go.etcd.io/etcd/server/v3/features"
)
@@ -44,7 +43,7 @@ func TestConfigParsingMemberFlags(t *testing.T) {
"-max-wals=10",
"-max-snapshots=10",
"-snapshot-count=10",
- "-experimental-snapshot-catchup-entries=1000",
+ "-snapshot-catchup-entries=1000",
"-listen-peer-urls=http://localhost:8000,https://localhost:8001",
"-listen-client-urls=http://localhost:7000,https://localhost:7001",
"-listen-client-http-urls=http://localhost:7002,https://localhost:7003",
@@ -210,21 +209,21 @@ func TestConfigFileClusteringFlags(t *testing.T) {
func TestConfigParsingConflictClusteringFlags(t *testing.T) {
conflictArgs := [][]string{
{
- "-initial-cluster=0=localhost:8000",
- "-discovery=http://example.com/abc",
+ "--initial-cluster=0=localhost:8000",
+ "--discovery-endpoints=http://example.com/abc",
},
{
- "-discovery-srv=example.com",
- "-discovery=http://example.com/abc",
+ "--discovery-srv=example.com",
+ "--discovery-endpoints=http://example.com/abc",
},
{
- "-initial-cluster=0=localhost:8000",
- "-discovery-srv=example.com",
+ "--initial-cluster=0=localhost:8000",
+ "--discovery-srv=example.com",
},
{
- "-initial-cluster=0=localhost:8000",
- "-discovery=http://example.com/abc",
- "-discovery-srv=example.com",
+ "--initial-cluster=0=localhost:8000",
+ "--discovery-endpoints=http://example.com/abc",
+ "--discovery-srv=example.com",
},
}
@@ -238,17 +237,21 @@ func TestConfigParsingConflictClusteringFlags(t *testing.T) {
func TestConfigFileConflictClusteringFlags(t *testing.T) {
tests := []struct {
- InitialCluster string `json:"initial-cluster"`
- DNSCluster string `json:"discovery-srv"`
- Durl string `json:"discovery"`
+ InitialCluster string `json:"initial-cluster"`
+ DNSCluster string `json:"discovery-srv"`
+ DiscoveryCfg v3discovery.DiscoveryConfig `json:"discovery-config"`
}{
{
InitialCluster: "0=localhost:8000",
- Durl: "http://example.com/abc",
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{Endpoints: []string{"http://example.com/abc"}},
+ },
},
{
DNSCluster: "example.com",
- Durl: "http://example.com/abc",
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{Endpoints: []string{"http://example.com/abc"}},
+ },
},
{
InitialCluster: "0=localhost:8000",
@@ -256,8 +259,10 @@ func TestConfigFileConflictClusteringFlags(t *testing.T) {
},
{
InitialCluster: "0=localhost:8000",
- Durl: "http://example.com/abc",
- DNSCluster: "example.com",
+ DiscoveryCfg: v3discovery.DiscoveryConfig{
+ ConfigSpec: clientv3.ConfigSpec{Endpoints: []string{"http://example.com/abc"}},
+ },
+ DNSCluster: "example.com",
},
}
@@ -286,29 +291,28 @@ func TestConfigParsingMissedAdvertiseClientURLsFlag(t *testing.T) {
}{
{
[]string{
- "-initial-cluster=infra1=http://127.0.0.1:2380",
- "-listen-client-urls=http://127.0.0.1:2379",
+ "--initial-cluster=infra1=http://127.0.0.1:2380",
+ "--listen-client-urls=http://127.0.0.1:2379",
},
embed.ErrUnsetAdvertiseClientURLsFlag,
},
{
[]string{
- "-discovery-srv=example.com",
- "-listen-client-urls=http://127.0.0.1:2379",
+ "--discovery-srv=example.com",
+ "--listen-client-urls=http://127.0.0.1:2379",
},
embed.ErrUnsetAdvertiseClientURLsFlag,
},
{
[]string{
- "-discovery=http://example.com/abc",
- "-discovery-fallback=exit",
- "-listen-client-urls=http://127.0.0.1:2379",
+ "--discovery-fallback=exit",
+ "--listen-client-urls=http://127.0.0.1:2379",
},
embed.ErrUnsetAdvertiseClientURLsFlag,
},
{
[]string{
- "-listen-client-urls=http://127.0.0.1:2379",
+ "--listen-client-urls=http://127.0.0.1:2379",
},
embed.ErrUnsetAdvertiseClientURLsFlag,
},
@@ -322,73 +326,6 @@ func TestConfigParsingMissedAdvertiseClientURLsFlag(t *testing.T) {
}
}
-// TestExperimentalSnapshotCatchUpEntriesFlagMigration tests the migration from
-// --experimental-snapshot-catch-up-entries to --snapshot-catch-up-entries
-func TestExperimentalSnapshotCatchUpEntriesFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- snapshotCatchUpEntries uint64
- experimentalSnapshotCatchUpEntries uint64
- wantErr bool
- wantConfig uint64
- }{
- {
- name: "default",
- wantConfig: etcdserver.DefaultSnapshotCatchUpEntries,
- },
- {
- name: "cannot set both experimental flag and non experimental flag",
- experimentalSnapshotCatchUpEntries: 1000,
- snapshotCatchUpEntries: 2000,
- wantErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalSnapshotCatchUpEntries: 1000,
- wantConfig: 1000,
- },
- {
- name: "can set non-experimental flag",
- snapshotCatchUpEntries: 2000,
- wantConfig: 2000,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalSnapshotCatchUpEntries uint64 `json:"experimental-snapshot-catch-up-entries,omitempty"`
- SnapshotCatchUpEntries uint64 `json:"snapshot-catchup-entries,omitempty"`
- }{}
-
- if tc.snapshotCatchUpEntries > 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--snapshot-catchup-entries=%d", tc.snapshotCatchUpEntries))
- yc.SnapshotCatchUpEntries = tc.snapshotCatchUpEntries
- }
-
- if tc.experimentalSnapshotCatchUpEntries > 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-snapshot-catchup-entries=%d", tc.experimentalSnapshotCatchUpEntries))
- yc.ExperimentalSnapshotCatchUpEntries = tc.experimentalSnapshotCatchUpEntries
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.wantErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.wantConfig, cfgFromCmdLine.ec.SnapshotCatchUpEntries)
- require.Equal(t, tc.wantConfig, cfgFromFile.ec.SnapshotCatchUpEntries)
- })
- }
-}
-
func TestConfigIsNewCluster(t *testing.T) {
tests := []struct {
state string
@@ -482,35 +419,6 @@ func TestParseFeatureGateFlags(t *testing.T) {
features.StopGRPCServiceOnDefrag: false,
},
},
- {
- name: "cannot set both experimental flag and feature gate flag",
- args: []string{
- "--experimental-stop-grpc-service-on-defrag=false",
- "--feature-gates=StopGRPCServiceOnDefrag=true",
- },
- expectErr: true,
- },
- {
- name: "ok to set different experimental flag and feature gate flag",
- args: []string{
- "--experimental-stop-grpc-service-on-defrag=true",
- "--feature-gates=InitialCorruptCheck=true",
- },
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- features.InitialCorruptCheck: true,
- },
- },
- {
- name: "can set feature gate from experimental flag",
- args: []string{
- "--experimental-stop-grpc-service-on-defrag=true",
- },
- expectedFeatures: map[featuregate.Feature]bool{
- features.StopGRPCServiceOnDefrag: true,
- features.InitialCorruptCheck: false,
- },
- },
{
name: "can set feature gate from feature gate flag",
args: []string{
@@ -543,643 +451,8 @@ func TestParseFeatureGateFlags(t *testing.T) {
}
}
-// TestDowngradeCheckTimeFlagMigration tests the migration from
-// --experimental-downgrade-check-time to --downgrade-check-time
-func TestDowngradeCheckTimeFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- downgradeCheckTime string
- experimentalDowngradeCheckTime string
- wantErr bool
- wantConfig time.Duration
- }{
- {
- name: "default",
- wantConfig: embed.DefaultDowngradeCheckTime,
- },
- {
- name: "cannot set both experimental flag and non experimental flag",
- experimentalDowngradeCheckTime: "1m",
- downgradeCheckTime: "2m",
- wantErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalDowngradeCheckTime: "1m",
- wantConfig: time.Minute,
- },
- {
- name: "can set non-experimental flag",
- downgradeCheckTime: "2m",
- wantConfig: 2 * time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalDowngradeCheckTime time.Duration `json:"experimental-downgrade-check-time,omitempty"`
- DowngradeCheckTime time.Duration `json:"downgrade-check-time,omitempty"`
- }{}
-
- if tc.downgradeCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--downgrade-check-time=%s", tc.downgradeCheckTime))
- downgradeCheckTime, err := time.ParseDuration(tc.downgradeCheckTime)
- require.NoError(t, err)
- yc.DowngradeCheckTime = downgradeCheckTime
- }
-
- if tc.experimentalDowngradeCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-downgrade-check-time=%s", tc.experimentalDowngradeCheckTime))
- experimentalDowngradeCheckTime, err := time.ParseDuration(tc.experimentalDowngradeCheckTime)
- require.NoError(t, err)
- yc.ExperimentalDowngradeCheckTime = experimentalDowngradeCheckTime
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.wantErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.wantConfig, cfgFromCmdLine.ec.DowngradeCheckTime)
- require.Equal(t, tc.wantConfig, cfgFromFile.ec.DowngradeCheckTime)
- })
- }
-}
-
-// TestCompactHashCheckTimeFlagMigration tests the migration from
-// --experimental-compact-hash-check-time to --compact-hash-check-time
-// TODO: delete in v3.7
-func TestCompactHashCheckTimeFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- compactHashCheckTime string
- experimentalCompactHashCheckTime string
- expectErr bool
- expectedCompactHashCheckTime time.Duration
- }{
- {
- name: "default",
- expectedCompactHashCheckTime: time.Minute,
- },
- {
- name: "cannot set both experimental flag and non experimental flag",
- compactHashCheckTime: "2m",
- experimentalCompactHashCheckTime: "3m",
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalCompactHashCheckTime: "3m",
- expectedCompactHashCheckTime: 3 * time.Minute,
- },
- {
- name: "can set non experimental flag",
- compactHashCheckTime: "2m",
- expectedCompactHashCheckTime: 2 * time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalCompactHashCheckTime time.Duration `json:"experimental-compact-hash-check-time,omitempty"`
- CompactHashCheckTime time.Duration `json:"compact-hash-check-time,omitempty"`
- }{}
-
- if tc.compactHashCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--compact-hash-check-time=%s", tc.compactHashCheckTime))
- compactHashCheckTime, err := time.ParseDuration(tc.compactHashCheckTime)
- require.NoError(t, err)
- yc.CompactHashCheckTime = compactHashCheckTime
- }
-
- if tc.experimentalCompactHashCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-compact-hash-check-time=%s", tc.experimentalCompactHashCheckTime))
- experimentalCompactHashCheckTime, err := time.ParseDuration(tc.experimentalCompactHashCheckTime)
- require.NoError(t, err)
- yc.ExperimentalCompactHashCheckTime = experimentalCompactHashCheckTime
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedCompactHashCheckTime, cfgFromCmdLine.ec.CompactHashCheckTime)
- require.Equal(t, tc.expectedCompactHashCheckTime, cfgFromFile.ec.CompactHashCheckTime)
- })
- }
-}
-
-// TestCompactionSleepIntervalFlagMigration tests the migration from
-// --experimental-compaction-sleep-interval to --compaction-sleep-interval
-func TestCompactionSleepIntervalFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- compactionSleepInterval string
- experimentalCompactionSleepInterval string
- wantErr bool
- wantConfig time.Duration
- }{
- {
- name: "default",
- wantConfig: time.Duration(0),
- },
- {
- name: "cannot set both experimental flag and non experimental flag",
- experimentalCompactionSleepInterval: "30s",
- compactionSleepInterval: "15s",
- wantErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalCompactionSleepInterval: "30s",
- wantConfig: 30 * time.Second,
- },
- {
- name: "can set non-experimental flag",
- compactionSleepInterval: "1m",
- wantConfig: time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalCompactionSleepInterval time.Duration `json:"experimental-compaction-sleep-interval,omitempty"`
- CompactionSleepInterval time.Duration `json:"compaction-sleep-interval,omitempty"`
- }{}
-
- if tc.compactionSleepInterval != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--compaction-sleep-interval=%s", tc.compactionSleepInterval))
- compactionSleepInterval, err := time.ParseDuration(tc.compactionSleepInterval)
- require.NoError(t, err)
- yc.CompactionSleepInterval = compactionSleepInterval
- }
-
- if tc.experimentalCompactionSleepInterval != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-compaction-sleep-interval=%s", tc.experimentalCompactionSleepInterval))
- experimentalCompactionSleepInterval, err := time.ParseDuration(tc.experimentalCompactionSleepInterval)
- require.NoError(t, err)
- yc.ExperimentalCompactionSleepInterval = experimentalCompactionSleepInterval
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.wantErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.wantConfig, cfgFromCmdLine.ec.CompactionSleepInterval)
- require.Equal(t, tc.wantConfig, cfgFromFile.ec.CompactionSleepInterval)
- })
- }
-}
-
-// TestCorruptCheckTimeFlagMigration tests the migration from
-// --experimental-corrupt-check-time to --corrupt-check-time
-// TODO: delete in v3.7
-func TestCorruptCheckTimeFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- corruptCheckTime string
- experimentalCorruptCheckTime string
- expectErr bool
- expectedCorruptCheckTime time.Duration
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- corruptCheckTime: "2m",
- experimentalCorruptCheckTime: "3m",
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalCorruptCheckTime: "3m",
- expectedCorruptCheckTime: 3 * time.Minute,
- },
- {
- name: "can set non experimental flag",
- corruptCheckTime: "2m",
- expectedCorruptCheckTime: 2 * time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time,omitempty"`
- CorruptCheckTime time.Duration `json:"corrupt-check-time,omitempty"`
- }{}
-
- if tc.corruptCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--corrupt-check-time=%s", tc.corruptCheckTime))
- corruptCheckTime, err := time.ParseDuration(tc.corruptCheckTime)
- require.NoError(t, err)
- yc.CorruptCheckTime = corruptCheckTime
- }
-
- if tc.experimentalCorruptCheckTime != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-corrupt-check-time=%s", tc.experimentalCorruptCheckTime))
- experimentalCorruptCheckTime, err := time.ParseDuration(tc.experimentalCorruptCheckTime)
- require.NoError(t, err)
- yc.ExperimentalCorruptCheckTime = experimentalCorruptCheckTime
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedCorruptCheckTime, cfgFromCmdLine.ec.CorruptCheckTime)
- require.Equal(t, tc.expectedCorruptCheckTime, cfgFromFile.ec.CorruptCheckTime)
- })
- }
-}
-
-// TestCompactionBatchLimitFlagMigration tests the migration from
-// --experimental-compaction-batch-limit to --compaction-batch-limit
-// TODO: delete in v3.7
-func TestCompactionBatchLimitFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- compactionBatchLimit int
- experimentalCompactionBatchLimit int
- expectErr bool
- expectedCompactionBatchLimit int
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- compactionBatchLimit: 1,
- experimentalCompactionBatchLimit: 2,
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalCompactionBatchLimit: 2,
- expectedCompactionBatchLimit: 2,
- },
- {
- name: "can set non experimental flag",
- compactionBatchLimit: 1,
- expectedCompactionBatchLimit: 1,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit,omitempty"`
- CompactionBatchLimit int `json:"compaction-batch-limit,omitempty"`
- }{}
-
- if tc.compactionBatchLimit != 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--compaction-batch-limit=%d", tc.compactionBatchLimit))
- yc.CompactionBatchLimit = tc.compactionBatchLimit
- }
-
- if tc.experimentalCompactionBatchLimit != 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-compaction-batch-limit=%d", tc.experimentalCompactionBatchLimit))
- yc.ExperimentalCompactionBatchLimit = tc.experimentalCompactionBatchLimit
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedCompactionBatchLimit, cfgFromCmdLine.ec.CompactionBatchLimit)
- require.Equal(t, tc.expectedCompactionBatchLimit, cfgFromFile.ec.CompactionBatchLimit)
- })
- }
-}
-
-// TestWatchProgressNotifyInterval tests the migration from
-// --experimental-watch-progress-notify-interval to --watch-progress-notify-interval
-// TODO: delete in v3.7
-func TestWatchProgressNotifyInterval(t *testing.T) {
- testCases := []struct {
- name string
- watchProgressNotifyInterval string
- experimentalWatchProgressNotifyInterval string
- expectErr bool
- expectedWatchProgressNotifyInterval time.Duration
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- watchProgressNotifyInterval: "2m",
- experimentalWatchProgressNotifyInterval: "3m",
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalWatchProgressNotifyInterval: "3m",
- expectedWatchProgressNotifyInterval: 3 * time.Minute,
- },
- {
- name: "can set non experimental flag",
- watchProgressNotifyInterval: "2m",
- expectedWatchProgressNotifyInterval: 2 * time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval,omitempty"`
- WatchProgressNotifyInterval time.Duration `json:"watch-progress-notify-interval,omitempty"`
- }{}
-
- if tc.watchProgressNotifyInterval != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--watch-progress-notify-interval=%s", tc.watchProgressNotifyInterval))
- watchProgressNotifyInterval, err := time.ParseDuration(tc.watchProgressNotifyInterval)
- require.NoError(t, err)
- yc.WatchProgressNotifyInterval = watchProgressNotifyInterval
- }
-
- if tc.experimentalWatchProgressNotifyInterval != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-watch-progress-notify-interval=%s", tc.experimentalWatchProgressNotifyInterval))
- experimentalWatchProgressNotifyInterval, err := time.ParseDuration(tc.experimentalWatchProgressNotifyInterval)
- require.NoError(t, err)
- yc.ExperimentalWatchProgressNotifyInterval = experimentalWatchProgressNotifyInterval
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedWatchProgressNotifyInterval, cfgFromCmdLine.ec.WatchProgressNotifyInterval)
- require.Equal(t, tc.expectedWatchProgressNotifyInterval, cfgFromFile.ec.WatchProgressNotifyInterval)
- })
- }
-}
-
-// TestWarningApplyDuration tests the migration from
-// --experimental-warning-apply-duration to --warning-apply-duration
-// TODO: delete in v3.7
-func TestWarningApplyDuration(t *testing.T) {
- testCases := []struct {
- name string
- warningApplyDuration string
- experimentalWarningApplyDuration string
- expectErr bool
- expectedWarningApplyDuration time.Duration
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- warningApplyDuration: "2m",
- experimentalWarningApplyDuration: "3m",
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalWarningApplyDuration: "3m",
- expectedWarningApplyDuration: 3 * time.Minute,
- },
- {
- name: "can set non experimental flag",
- warningApplyDuration: "2m",
- expectedWarningApplyDuration: 2 * time.Minute,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration,omitempty"`
- WarningApplyDuration time.Duration `json:"warning-apply-duration,omitempty"`
- }{}
-
- if tc.warningApplyDuration != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--warning-apply-duration=%s", tc.warningApplyDuration))
- warningApplyDuration, err := time.ParseDuration(tc.warningApplyDuration)
- require.NoError(t, err)
- yc.WarningApplyDuration = warningApplyDuration
- }
-
- if tc.experimentalWarningApplyDuration != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-warning-apply-duration=%s", tc.experimentalWarningApplyDuration))
- experimentalWarningApplyDuration, err := time.ParseDuration(tc.experimentalWarningApplyDuration)
- require.NoError(t, err)
- yc.ExperimentalWarningApplyDuration = experimentalWarningApplyDuration
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedWarningApplyDuration, cfgFromCmdLine.ec.WarningApplyDuration)
- require.Equal(t, tc.expectedWarningApplyDuration, cfgFromFile.ec.WarningApplyDuration)
- })
- }
-}
-
-// TestBootstrapDefragThresholdMegabytesFlagMigration tests the migration from
-// --experimental-bootstrap-defrag-threshold-megabytes to --bootstrap-defrag-threshold-megabytes
-// TODO: delete in v3.7
-func TestBootstrapDefragThresholdMegabytesFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- bootstrapDefragThresholdMegabytes uint
- experimentalBootstrapDefragThresholdMegabytes uint
- expectErr bool
- expectedBootstrapDefragThresholdMegabytes uint
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- bootstrapDefragThresholdMegabytes: 100,
- experimentalBootstrapDefragThresholdMegabytes: 200,
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalBootstrapDefragThresholdMegabytes: 200,
- expectedBootstrapDefragThresholdMegabytes: 200,
- },
- {
- name: "can set non experimental flag",
- bootstrapDefragThresholdMegabytes: 100,
- expectedBootstrapDefragThresholdMegabytes: 100,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes,omitempty"`
- BootstrapDefragThresholdMegabytes uint `json:"bootstrap-defrag-threshold-megabytes,omitempty"`
- }{}
-
- if tc.bootstrapDefragThresholdMegabytes != 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--bootstrap-defrag-threshold-megabytes=%d", tc.bootstrapDefragThresholdMegabytes))
- yc.BootstrapDefragThresholdMegabytes = tc.bootstrapDefragThresholdMegabytes
- }
-
- if tc.experimentalBootstrapDefragThresholdMegabytes != 0 {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-bootstrap-defrag-threshold-megabytes=%d", tc.experimentalBootstrapDefragThresholdMegabytes))
- yc.ExperimentalBootstrapDefragThresholdMegabytes = tc.experimentalBootstrapDefragThresholdMegabytes
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- require.Equal(t, tc.expectedBootstrapDefragThresholdMegabytes, cfgFromCmdLine.ec.BootstrapDefragThresholdMegabytes)
- require.Equal(t, tc.expectedBootstrapDefragThresholdMegabytes, cfgFromFile.ec.BootstrapDefragThresholdMegabytes)
- })
- }
-}
-
-// TestMemoryMlockFlagMigration tests the migration from
-// --experimental-memory-mlock to --memory-mlock
-// TODO: delete in v3.7
-func TestMemoryMlockFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- memoryMlock bool
- experimentalMemoryMlock bool
- expectedMemoryMlock bool
- expectErr bool
- }{
- {
- name: "default",
- expectedMemoryMlock: false,
- },
- {
- name: "cannot set both experimental flag and non experimental flag",
- memoryMlock: true,
- experimentalMemoryMlock: true,
- expectErr: true,
- },
- {
- name: "can set experimental flag",
- experimentalMemoryMlock: true,
- expectedMemoryMlock: true,
- },
- {
- name: "can set non experimental flag",
- memoryMlock: true,
- expectedMemoryMlock: true,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- MemoryMlock bool `json:"memory-mlock,omitempty"`
- ExperimentalMemoryMlock bool `json:"experimental-memory-mlock,omitempty"`
- }{}
-
- if tc.memoryMlock {
- cmdLineArgs = append(cmdLineArgs, "--memory-mlock")
- yc.MemoryMlock = tc.memoryMlock
- }
-
- if tc.experimentalMemoryMlock {
- cmdLineArgs = append(cmdLineArgs, "--experimental-memory-mlock")
- yc.ExperimentalMemoryMlock = tc.experimentalMemoryMlock
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatal("expect parse error")
- }
- return
- }
-
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- if cfgFromCmdLine.ec.MemoryMlock != tc.expectedMemoryMlock {
- t.Errorf("expected MemoryMlock=%v, got %v", tc.expectedMemoryMlock, cfgFromCmdLine.ec.MemoryMlock)
- }
- if cfgFromFile.ec.MemoryMlock != tc.expectedMemoryMlock {
- t.Errorf("expected MemoryMlock=%v, got %v", tc.expectedMemoryMlock, cfgFromFile.ec.MemoryMlock)
- }
- })
- }
-}
-
-// TODO delete in v3.7
-func generateCfgsFromFileAndCmdLine(t *testing.T, yc any, cmdLineArgs []string) (*config, error, *config, error) {
- b, err := yaml.Marshal(&yc)
- require.NoError(t, err)
-
- tmpfile := mustCreateCfgFile(t, b)
- defer os.Remove(tmpfile.Name())
-
- cfgFromCmdLine := newConfig()
- errFromCmdLine := cfgFromCmdLine.parse(cmdLineArgs)
-
- cfgFromFile := newConfig()
- errFromFile := cfgFromFile.parse([]string{fmt.Sprintf("--config-file=%s", tmpfile.Name())})
- return cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile
-}
-
func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
- tmpfile, err := os.CreateTemp("", "servercfg")
+ tmpfile, err := os.CreateTemp(t.TempDir(), "servercfg")
if err != nil {
t.Fatal(err)
}
@@ -1266,19 +539,8 @@ func validateClusteringFlags(t *testing.T, cfg *config) {
func TestConfigFileDeprecatedOptions(t *testing.T) {
// Define a minimal config struct with only the fields we need
type configFileYAML struct {
- SnapshotCount uint64 `json:"snapshot-count,omitempty"`
- MaxSnapFiles uint `json:"max-snapshots,omitempty"`
- ExperimentalCompactHashCheckEnabled bool `json:"experimental-compact-hash-check-enabled,omitempty"`
- ExperimentalCompactHashCheckTime time.Duration `json:"experimental-compact-hash-check-time,omitempty"`
- ExperimentalWarningUnaryRequestDuration time.Duration `json:"experimental-warning-unary-request-duration,omitempty"`
- ExperimentalCorruptCheckTime time.Duration `json:"experimental-corrupt-check-time,omitempty"`
- ExperimentalCompactionBatchLimit int `json:"experimental-compaction-batch-limit,omitempty"`
- ExperimentalWatchProgressNotifyInterval time.Duration `json:"experimental-watch-progress-notify-interval,omitempty"`
- ExperimentalWarningApplyDuration time.Duration `json:"experimental-warning-apply-duration,omitempty"`
- ExperimentalBootstrapDefragThresholdMegabytes uint `json:"experimental-bootstrap-defrag-threshold-megabytes,omitempty"`
- ExperimentalSnapshotCatchUpEntries uint64 `json:"experimental-snapshot-catch-up-entries,omitempty"`
- ExperimentalCompactionSleepInterval time.Duration `json:"experimental-compaction-sleep-interval,omitempty"`
- ExperimentalDowngradeCheckTime time.Duration `json:"experimental-downgrade-check-time,omitempty"`
+ SnapshotCount uint64 `json:"snapshot-count,omitempty"`
+ MaxSnapFiles uint `json:"max-snapshots,omitempty"`
}
testCases := []struct {
@@ -1291,34 +553,6 @@ func TestConfigFileDeprecatedOptions(t *testing.T) {
configFileYAML: configFileYAML{},
expectedFlags: map[string]struct{}{},
},
- {
- name: "deprecated experimental options",
- configFileYAML: configFileYAML{
- ExperimentalCompactHashCheckEnabled: true,
- ExperimentalCompactHashCheckTime: 2 * time.Minute,
- ExperimentalWarningUnaryRequestDuration: time.Second,
- ExperimentalCorruptCheckTime: time.Minute,
- ExperimentalCompactionBatchLimit: 1,
- ExperimentalWatchProgressNotifyInterval: 3 * time.Minute,
- ExperimentalWarningApplyDuration: 3 * time.Minute,
- ExperimentalBootstrapDefragThresholdMegabytes: 100,
- ExperimentalSnapshotCatchUpEntries: 1000,
- ExperimentalCompactionSleepInterval: 30 * time.Second,
- ExperimentalDowngradeCheckTime: 1 * time.Minute,
- },
- expectedFlags: map[string]struct{}{
- "experimental-compact-hash-check-enabled": {},
- "experimental-compact-hash-check-time": {},
- "experimental-corrupt-check-time": {},
- "experimental-compaction-batch-limit": {},
- "experimental-watch-progress-notify-interval": {},
- "experimental-warning-apply-duration": {},
- "experimental-bootstrap-defrag-threshold-megabytes": {},
- "experimental-snapshot-catchup-entries": {},
- "experimental-compaction-sleep-interval": {},
- "experimental-downgrade-check-time": {},
- },
- },
{
name: "deprecated snapshot options",
configFileYAML: configFileYAML{
@@ -1360,349 +594,6 @@ func TestConfigFileDeprecatedOptions(t *testing.T) {
// Compare sets of flags
assert.Equalf(t, tc.expectedFlags, foundFlags, "deprecated flags mismatch - expected: %v, got: %v",
tc.expectedFlags, foundFlags)
-
- // Note: experimental-warning-unary-request-duration deprecation is handled
- // through a separate mechanism in embed.Config
- if tc.configFileYAML.ExperimentalWarningUnaryRequestDuration != 0 {
- assert.Equalf(t, cfg.ec.WarningUnaryRequestDuration,
- tc.configFileYAML.ExperimentalWarningUnaryRequestDuration,
- "experimental warning duration mismatch - expected: %v, got: %v",
- tc.configFileYAML.ExperimentalWarningUnaryRequestDuration,
- cfg.ec.WarningUnaryRequestDuration)
- }
- })
- }
-}
-
-// TestPeerSkipClientSanVerificationFlagMigration tests the migration from
-// --experimental-peer-skip-client-san-verification to --peer-skip-client-san-verification
-// TODO: delete in v3.7
-func TestPeerSkipClientSanVerificationFlagMigration(t *testing.T) {
- testCases := []struct {
- name string
- peerSkipClientSanVerification string
- experimentalPeerSkipClientSanVerification string
- useConfigFile bool
- expectErr bool
- expectedPeerSkipClientSanVerification bool
- }{
- {
- name: "cannot set both experimental flag and non experimental flag",
- peerSkipClientSanVerification: "true",
- experimentalPeerSkipClientSanVerification: "true",
- expectErr: true,
- },
- {
- name: "can set experimental flag to true",
- experimentalPeerSkipClientSanVerification: "true",
- expectedPeerSkipClientSanVerification: true,
- },
- {
- name: "can set experimental flag to false",
- experimentalPeerSkipClientSanVerification: "false",
- expectedPeerSkipClientSanVerification: false,
- },
- {
- name: "can set non experimental flag to true",
- peerSkipClientSanVerification: "true",
- expectedPeerSkipClientSanVerification: true,
- },
- {
- name: "can set non experimental flag to false",
- peerSkipClientSanVerification: "false",
- expectedPeerSkipClientSanVerification: false,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- type securityConfig struct {
- SkipClientSanVerification bool `json:"skip-client-san-verification,omitempty"`
- }
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalPeerSkipClientSanVerification bool `json:"experimental-peer-skip-client-san-verification,omitempty"`
- PeerSecurityJSON securityConfig `json:"peer-transport-security"`
- }{}
-
- if tc.peerSkipClientSanVerification != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--peer-skip-client-san-verification=%s", tc.peerSkipClientSanVerification))
- val, err := strconv.ParseBool(tc.peerSkipClientSanVerification)
- if err != nil {
- t.Fatal(err)
- }
- yc.PeerSecurityJSON.SkipClientSanVerification = val
- }
-
- if tc.experimentalPeerSkipClientSanVerification != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-peer-skip-client-san-verification=%s", tc.experimentalPeerSkipClientSanVerification))
- val, err := strconv.ParseBool(tc.experimentalPeerSkipClientSanVerification)
- if err != nil {
- t.Fatal(err)
- }
- yc.ExperimentalPeerSkipClientSanVerification = val
- }
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatalf("expect parse error, got errFromCmdLine=%v, errFromFile=%v", errFromCmdLine, errFromFile)
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
- if cfgFromCmdLine.ec.PeerTLSInfo.SkipClientSANVerify != tc.expectedPeerSkipClientSanVerification {
- t.Errorf("expected SkipClientSANVerify=%v, got %v",
- tc.expectedPeerSkipClientSanVerification,
- cfgFromCmdLine.ec.PeerTLSInfo.SkipClientSANVerify)
- }
- if cfgFromFile.ec.PeerTLSInfo.SkipClientSANVerify != tc.expectedPeerSkipClientSanVerification {
- t.Errorf("expected SkipClientSANVerify=%v, got %v",
- tc.expectedPeerSkipClientSanVerification,
- cfgFromFile.ec.PeerTLSInfo.SkipClientSANVerify)
- }
- })
- }
-}
-
-// TestDistributedTracingFlagsMigration tests the migration from
-// --experimental-distributed-tracing-* to --distributed-tracing-*
-// TODO: delete in v3.7
-func TestDistributedTracingFlagsMigration(t *testing.T) {
- testCases := []struct {
- name string
-
- enableDistributedTracing string
- distributedTracingAddress string
- distributedTracingServiceName string
- distributedTracingServiceInstanceID string
- distributedTracingSamplingRatePerMillion string
-
- experimentalEnableDistributedTracing string
- experimentalDistributedTracingAddress string
- experimentalDistributedTracingServiceName string
- experimentalDistributedTracingServiceInstanceID string
- experimentalDistributedTracingSamplingRatePerMillion string
-
- expectedEnableDistributedTracing bool
- expectedDistributedTracingAddress string
- expectedDistributedTracingServiceName string
- expectedDistributedTracingServiceInstanceID string
- expectedDistributedTracingSamplingRatePerMillion int
-
- expectErr bool
- }{
- // cannot set both experimental flags and non-experimental flags
- {
- name: "cannot set both experimental flag and non experimental flag 1",
- enableDistributedTracing: "true",
- experimentalEnableDistributedTracing: "true",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and non experimental flag 2",
- distributedTracingAddress: "localhost:4317",
- experimentalDistributedTracingAddress: "localhost:4318",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and non experimental flag 3",
- distributedTracingServiceName: "etcd1",
- experimentalDistributedTracingServiceName: "etcd2",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and non experimental flag 4",
- distributedTracingServiceInstanceID: "fakeID",
- experimentalDistributedTracingServiceInstanceID: "fakeID",
- expectErr: true,
- },
- {
- name: "cannot set both experimental flag and non experimental flag 5",
- distributedTracingSamplingRatePerMillion: "100",
- experimentalDistributedTracingSamplingRatePerMillion: "100",
- expectErr: true,
- },
- // can set either --experimental-enable-distributed-tracing or --enable-distributed-tracing
- {
- name: "can set experimental-enable-distributed-tracing to true",
- experimentalEnableDistributedTracing: "true",
- expectedEnableDistributedTracing: true,
- },
- {
- name: "can set experimental-enable-distributed-tracing to false",
- experimentalEnableDistributedTracing: "false",
- expectedEnableDistributedTracing: false,
- },
- {
- name: "can set enable-distributed-tracing to true",
- enableDistributedTracing: "true",
- expectedEnableDistributedTracing: true,
- },
- {
- name: "can set enable-distributed-tracing to false",
- enableDistributedTracing: "false",
- expectedEnableDistributedTracing: false,
- },
- // can set either --experimental-distributed-tracing-address or --distributed-tracing-address
- {
- name: "can set experimental-distributed-tracing-address",
- experimentalDistributedTracingAddress: "localhost:1234",
- expectedDistributedTracingAddress: "localhost:1234",
- },
- {
- name: "can set distributed-tracing-address",
- distributedTracingAddress: "localhost:1234",
- expectedDistributedTracingAddress: "localhost:1234",
- },
- // can set either --experimental-distributed-tracing-service-name or --distributed-tracing-service-name
- {
- name: "can set experimental-distributed-tracing-service-name",
- experimentalDistributedTracingServiceName: "fakeSererName",
- expectedDistributedTracingServiceName: "fakeSererName",
- },
- {
- name: "can set distributed-tracing-service-name",
- distributedTracingServiceName: "fakeSererName",
- expectedDistributedTracingServiceName: "fakeSererName",
- },
- // can set either --experimental-distributed-tracing-instance-id or --distributed-tracing-instance-id
- {
- name: "can set experimental-distributed-tracing-instance-id",
- experimentalDistributedTracingServiceInstanceID: "fakeID",
- expectedDistributedTracingServiceInstanceID: "fakeID",
- },
- {
- name: "can set distributed-tracing-instance-id",
- distributedTracingServiceInstanceID: "fakeID",
- expectedDistributedTracingServiceInstanceID: "fakeID",
- },
- // can set either --experimental-distributed-tracing-sampling-rate or --distributed-tracing-sampling-rate
- {
- name: "can set experimental-distributed-tracing-sampling-rate",
- experimentalDistributedTracingSamplingRatePerMillion: "200",
- expectedDistributedTracingSamplingRatePerMillion: 200,
- },
- {
- name: "can set distributed-tracing-sampling-rate",
- distributedTracingSamplingRatePerMillion: "300",
- expectedDistributedTracingSamplingRatePerMillion: 300,
- },
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- cmdLineArgs := []string{}
- yc := struct {
- ExperimentalEnableDistributedTracing bool `json:"experimental-enable-distributed-tracing,omitempty"`
- EnableDistributedTracing bool `json:"enable-distributed-tracing,omitempty"`
-
- ExperimentalDistributedTracingAddress string `json:"experimental-distributed-tracing-address,omitempty"`
- DistributedTracingAddress string `json:"distributed-tracing-address,omitempty"`
-
- ExperimentalDistributedTracingServiceName string `json:"experimental-distributed-tracing-service-name,omitempty"`
- DistributedTracingServiceName string `json:"distributed-tracing-service-name,omitempty"`
-
- ExperimentalDistributedTracingServiceInstanceID string `json:"experimental-distributed-tracing-instance-id,omitempty"`
- DistributedTracingServiceInstanceID string `json:"distributed-tracing-instance-id,omitempty"`
-
- ExperimentalDistributedTracingSamplingRatePerMillion int `json:"experimental-distributed-tracing-sampling-rate,omitempty"`
- DistributedTracingSamplingRatePerMillion int `json:"distributed-tracing-sampling-rate,omitempty"`
- }{}
-
- // --enable-distributed-tracing and --experimental-enable-distributed-tracing
- if tc.enableDistributedTracing != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--enable-distributed-tracing=%s", tc.enableDistributedTracing))
- val, err := strconv.ParseBool(tc.enableDistributedTracing)
- require.NoError(t, err)
- yc.EnableDistributedTracing = val
- }
- if tc.experimentalEnableDistributedTracing != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-enable-distributed-tracing=%s", tc.experimentalEnableDistributedTracing))
- val, err := strconv.ParseBool(tc.experimentalEnableDistributedTracing)
- require.NoError(t, err)
- yc.ExperimentalEnableDistributedTracing = val
- }
-
- // --distributed-tracing-address and --experimental-distributed-tracing-address
- if tc.distributedTracingAddress != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--distributed-tracing-address=%s", tc.distributedTracingAddress))
- yc.DistributedTracingAddress = tc.distributedTracingAddress
- }
- if tc.experimentalDistributedTracingAddress != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-distributed-tracing-address=%s", tc.experimentalDistributedTracingAddress))
- yc.ExperimentalDistributedTracingAddress = tc.experimentalDistributedTracingAddress
- }
-
- // --distributed-tracing-service-name and --experimental-distributed-tracing-service-name
- if tc.distributedTracingServiceName != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--distributed-tracing-service-name=%s", tc.distributedTracingServiceName))
- yc.DistributedTracingServiceName = tc.distributedTracingServiceName
- }
- if tc.experimentalDistributedTracingServiceName != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-distributed-tracing-service-name=%s", tc.experimentalDistributedTracingServiceName))
- yc.ExperimentalDistributedTracingServiceName = tc.experimentalDistributedTracingServiceName
- }
-
- // --distributed-tracing-instance-id and --experimental-distributed-tracing-instance-id
- if tc.distributedTracingServiceInstanceID != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--distributed-tracing-instance-id=%s", tc.distributedTracingServiceInstanceID))
- yc.DistributedTracingServiceInstanceID = tc.distributedTracingServiceInstanceID
- }
- if tc.experimentalDistributedTracingServiceInstanceID != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-distributed-tracing-instance-id=%s", tc.experimentalDistributedTracingServiceInstanceID))
- yc.ExperimentalDistributedTracingServiceInstanceID = tc.experimentalDistributedTracingServiceInstanceID
- }
-
- // --distributed-tracing-sampling-rate and --experimental-distributed-tracing-sampling-rate
- if tc.distributedTracingSamplingRatePerMillion != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--distributed-tracing-sampling-rate=%s", tc.distributedTracingSamplingRatePerMillion))
- val, err := strconv.ParseInt(tc.distributedTracingSamplingRatePerMillion, 10, 64)
- require.NoError(t, err)
- yc.DistributedTracingSamplingRatePerMillion = int(val)
- }
- if tc.experimentalDistributedTracingSamplingRatePerMillion != "" {
- cmdLineArgs = append(cmdLineArgs, fmt.Sprintf("--experimental-distributed-tracing-sampling-rate=%s", tc.experimentalDistributedTracingSamplingRatePerMillion))
- val, err := strconv.ParseInt(tc.experimentalDistributedTracingSamplingRatePerMillion, 10, 64)
- require.NoError(t, err)
- yc.ExperimentalDistributedTracingSamplingRatePerMillion = int(val)
- }
-
- cfgFromCmdLine, errFromCmdLine, cfgFromFile, errFromFile := generateCfgsFromFileAndCmdLine(t, yc, cmdLineArgs)
-
- if tc.expectErr {
- if errFromCmdLine == nil || errFromFile == nil {
- t.Fatalf("expect parse error, got errFromCmdLine=%v, errFromFile=%v", errFromCmdLine, errFromFile)
- }
- return
- }
- if errFromCmdLine != nil || errFromFile != nil {
- t.Fatal("error parsing config")
- }
-
- // verify the expected values
- require.Equal(t, tc.expectedEnableDistributedTracing, cfgFromCmdLine.ec.EnableDistributedTracing)
- require.Equal(t, tc.expectedEnableDistributedTracing, cfgFromFile.ec.EnableDistributedTracing)
-
- if tc.expectedDistributedTracingAddress != "" {
- require.Equal(t, tc.expectedDistributedTracingAddress, cfgFromCmdLine.ec.DistributedTracingAddress)
- require.Equal(t, tc.expectedDistributedTracingAddress, cfgFromFile.ec.DistributedTracingAddress)
- }
-
- if tc.expectedDistributedTracingServiceName != "" {
- require.Equal(t, tc.expectedDistributedTracingServiceName, cfgFromCmdLine.ec.DistributedTracingServiceName)
- require.Equal(t, tc.expectedDistributedTracingServiceName, cfgFromFile.ec.DistributedTracingServiceName)
- }
-
- if tc.expectedDistributedTracingServiceInstanceID != "" {
- require.Equal(t, tc.expectedDistributedTracingServiceInstanceID, cfgFromCmdLine.ec.DistributedTracingServiceInstanceID)
- require.Equal(t, tc.expectedDistributedTracingServiceInstanceID, cfgFromFile.ec.DistributedTracingServiceInstanceID)
- }
-
- require.Equal(t, tc.expectedDistributedTracingSamplingRatePerMillion, cfgFromCmdLine.ec.DistributedTracingSamplingRatePerMillion)
- require.Equal(t, tc.expectedDistributedTracingSamplingRatePerMillion, cfgFromFile.ec.DistributedTracingSamplingRatePerMillion)
})
}
}
diff --git a/server/etcdmain/etcd.go b/server/etcdmain/etcd.go
index 16bba3736fd2..518ce8ff3679 100644
--- a/server/etcdmain/etcd.go
+++ b/server/etcdmain/etcd.go
@@ -29,7 +29,6 @@ import (
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/osutil"
"go.etcd.io/etcd/server/v3/embed"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2discovery"
"go.etcd.io/etcd/server/v3/etcdserver/errors"
)
@@ -64,8 +63,7 @@ func startEtcdOrProxyV2(args []string) {
lg.Info("Running: ", zap.Strings("args", args))
if err != nil {
lg.Warn("failed to verify flags", zap.Error(err))
- switch {
- case errorspkg.Is(err, embed.ErrUnsetAdvertiseClientURLsFlag):
+ if errorspkg.Is(err, embed.ErrUnsetAdvertiseClientURLsFlag) {
lg.Warn("advertise client URLs are not set", zap.Error(err))
}
os.Exit(1)
@@ -132,38 +130,14 @@ func startEtcdOrProxyV2(args []string) {
if err != nil {
var derr *errors.DiscoveryError
if errorspkg.As(err, &derr) {
- switch {
- case errorspkg.Is(derr.Err, v2discovery.ErrDuplicateID):
- lg.Warn(
- "member has been registered with discovery service",
- zap.String("name", cfg.ec.Name),
- zap.String("discovery-token", cfg.ec.Durl),
- zap.Error(derr.Err),
- )
- lg.Warn(
- "but could not find valid cluster configuration",
- zap.String("data-dir", cfg.ec.Dir),
- )
- lg.Warn("check data dir if previous bootstrap succeeded")
- lg.Warn("or use a new discovery token if previous bootstrap failed")
-
- case errorspkg.Is(derr.Err, v2discovery.ErrDuplicateName):
- lg.Warn(
- "member with duplicated name has already been registered",
- zap.String("discovery-token", cfg.ec.Durl),
- zap.Error(derr.Err),
- )
- lg.Warn("cURL the discovery token URL for details")
- lg.Warn("do not reuse discovery token; generate a new one to bootstrap a cluster")
+ lg.Warn(
+ "failed to bootstrap; discovery token was already used",
+ zap.String("discovery-token", cfg.ec.DiscoveryCfg.Token),
+ zap.Strings("discovery-endpoints", cfg.ec.DiscoveryCfg.Endpoints),
+ zap.Error(err),
+ )
+ lg.Warn("do not reuse discovery token; generate a new one to bootstrap a cluster")
- default:
- lg.Warn(
- "failed to bootstrap; discovery token was already used",
- zap.String("discovery-token", cfg.ec.Durl),
- zap.Error(err),
- )
- lg.Warn("do not reuse discovery token; generate a new one to bootstrap a cluster")
- }
os.Exit(1)
}
@@ -175,8 +149,8 @@ func startEtcdOrProxyV2(args []string) {
if types.URLs(cfg.ec.AdvertisePeerUrls).String() == embed.DefaultInitialAdvertisePeerURLs {
lg.Warn("forgot to set --initial-advertise-peer-urls?")
}
- if cfg.ec.InitialCluster == cfg.ec.InitialClusterFromName(cfg.ec.Name) && len(cfg.ec.Durl) == 0 && len(cfg.ec.DiscoveryCfg.Endpoints) == 0 {
- lg.Warn("V2 discovery settings (i.e., --discovery) or v3 discovery settings (i.e., --discovery-token, --discovery-endpoints) are not set")
+ if cfg.ec.InitialCluster == cfg.ec.InitialClusterFromName(cfg.ec.Name) && len(cfg.ec.DiscoveryCfg.Endpoints) == 0 {
+ lg.Warn("V3 discovery settings (i.e., --discovery-token, --discovery-endpoints) are not set")
}
os.Exit(1)
}
diff --git a/server/etcdmain/grpc_proxy.go b/server/etcdmain/grpc_proxy.go
index d012c1812167..f444a5f394c3 100644
--- a/server/etcdmain/grpc_proxy.go
+++ b/server/etcdmain/grpc_proxy.go
@@ -29,9 +29,8 @@ import (
"path/filepath"
"time"
- grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
- grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
+ "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
"github.com/prometheus/client_golang/prometheus"
"github.com/soheilhy/cmux"
"github.com/spf13/cobra"
@@ -384,9 +383,6 @@ func newProxyClientCfg(lg *zap.Logger, eps []string, tls *transport.TLSInfo) (*c
Endpoints: eps,
DialTimeout: 5 * time.Second,
Logger: lg,
- DialOptions: []grpc.DialOption{
- grpc.WithBlock(), //nolint:staticcheck // TODO: remove for a supported version
- },
}
if tls != nil {
clientTLS, err := tls.ClientConfig()
@@ -506,8 +502,6 @@ func newGRPCProxyServer(lg *zap.Logger, client *clientv3.Client) *grpc.Server {
electionp := grpcproxy.NewElectionProxy(client)
lockp := grpcproxy.NewLockProxy(client)
- alwaysLoggingDeciderServer := func(ctx context.Context, fullMethodName string, servingObject any) bool { return true }
-
serverMetrics := grpc_prometheus.NewServerMetrics()
prometheus.MustRegister(serverMetrics)
@@ -519,12 +513,10 @@ func newGRPCProxyServer(lg *zap.Logger, client *clientv3.Client) *grpc.Server {
}
if grpcProxyEnableLogging {
grpcChainStreamList = append(grpcChainStreamList,
- grpc_ctxtags.StreamServerInterceptor(),
- grpc_zap.PayloadStreamServerInterceptor(lg, alwaysLoggingDeciderServer),
+ interceptors.StreamServerInterceptor(reportable(lg)),
)
grpcChainUnaryList = append(grpcChainUnaryList,
- grpc_ctxtags.UnaryServerInterceptor(),
- grpc_zap.PayloadUnaryServerInterceptor(lg, alwaysLoggingDeciderServer),
+ interceptors.UnaryServerInterceptor(reportable(lg)),
)
}
diff --git a/server/etcdmain/grpc_proxy_logger.go b/server/etcdmain/grpc_proxy_logger.go
new file mode 100644
index 000000000000..329621e1bfc6
--- /dev/null
+++ b/server/etcdmain/grpc_proxy_logger.go
@@ -0,0 +1,100 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 etcdmain
+
+import (
+ "context"
+ "fmt"
+ "path"
+ "reflect"
+ "slices"
+ "time"
+
+ "github.com/golang/protobuf/jsonpb" //nolint:staticcheck // TODO: remove for a supported version
+ "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
+ "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
+ "go.uber.org/zap"
+ "google.golang.org/grpc/peer"
+)
+
+// grpcProxyLogger implements the go-grpc-middleware v2 Reporter interface
+type grpcProxyLogger struct {
+ logger *zap.Logger
+ fields []zap.Field
+}
+
+var _ interceptors.Reporter = (*grpcProxyLogger)(nil)
+
+const (
+ responseCallType = "response"
+ requestCallType = "request"
+)
+
+func (r *grpcProxyLogger) PostCall(_ error, _ time.Duration) {
+ // no op - no post-call payload logging
+}
+
+func (r *grpcProxyLogger) PostMsgReceive(payload any, err error, _ time.Duration) {
+ callType := requestCallType
+ f := logFieldsFromPayload(payload, err, callType)
+ r.logger.Info(fmt.Sprintf("received payload logged as grpc.%s.content field", callType), slices.Concat(f, r.fields)...)
+}
+
+func (r *grpcProxyLogger) PostMsgSend(payload any, err error, _ time.Duration) {
+ callType := responseCallType
+ f := logFieldsFromPayload(payload, err, callType)
+ r.logger.Info(fmt.Sprintf("returned response payload logged as grpc.%s.content field", callType), slices.Concat(f, r.fields)...)
+}
+
+func logFieldsFromPayload(payload any, err error, callType string) []zap.Field {
+ fields := []zap.Field{}
+ if err != nil {
+ fields = append(fields, zap.NamedError(fmt.Sprintf("grpc.%s.error", callType), err))
+ }
+ p, ok := payload.(proto.Message)
+ if !ok {
+ fields = append(fields, zap.NamedError("msg.type.error", fmt.Errorf("payload is not a github.com/golang/protobuf/proto message")))
+ }
+ msg, pErr := protoToJSON(p)
+ if pErr != nil {
+ fields = append(fields, zap.NamedError("msg.proto.error", fmt.Errorf("error when converting payload to logging json: %w", pErr)))
+ }
+ fields = append(fields, zap.String(fmt.Sprintf("grpc.%s.content", callType), msg))
+ return fields
+}
+
+func protoToJSON(msg proto.Message) (string, error) {
+ if reflect.ValueOf(msg).IsNil() {
+ return "", nil
+ }
+ marshaler := jsonpb.Marshaler{}
+ return marshaler.MarshalToString(msg)
+}
+
+func reportable(lg *zap.Logger) interceptors.CommonReportableFunc {
+ return func(ctx context.Context, c interceptors.CallMeta) (interceptors.Reporter, context.Context) {
+ fields := []zap.Field{
+ zap.String("grpc.service", path.Dir(c.FullMethod())[1:]),
+ zap.String("grpc.method", path.Base(c.FullMethod())),
+ }
+ if peer, ok := peer.FromContext(ctx); ok {
+ fields = append(fields, zap.String("peer.address", peer.Addr.String()))
+ }
+ return &grpcProxyLogger{
+ logger: lg,
+ fields: fields,
+ }, ctx
+ }
+}
diff --git a/server/etcdmain/grpc_proxy_logger_test.go b/server/etcdmain/grpc_proxy_logger_test.go
new file mode 100644
index 000000000000..9faed28299b8
--- /dev/null
+++ b/server/etcdmain/grpc_proxy_logger_test.go
@@ -0,0 +1,129 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 etcdmain
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "testing"
+ "time"
+
+ "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
+ "github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb"
+ "github.com/stretchr/testify/suite"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zaptest/observer"
+ "google.golang.org/grpc"
+)
+
+type loggingPayloadSuite struct {
+ *testpb.InterceptorTestSuite
+ logger *zap.Logger
+ logs *observer.ObservedLogs
+}
+
+func TestLoggingPayloadSuite(t *testing.T) {
+ observer, logs := observer.New(zap.InfoLevel)
+ logger := zap.New(observer)
+ s := &loggingPayloadSuite{
+ InterceptorTestSuite: &testpb.InterceptorTestSuite{
+ TestService: &testpb.TestPingService{},
+ ServerOpts: []grpc.ServerOption{
+ grpc.UnaryInterceptor(interceptors.UnaryServerInterceptor(reportable(logger))),
+ grpc.StreamInterceptor(interceptors.StreamServerInterceptor(reportable(logger))),
+ },
+ },
+ logs: logs,
+ logger: zap.New(observer),
+ }
+ suite.Run(t, s)
+}
+
+func (s *loggingPayloadSuite) SetupTest() {
+ s.logs.TakeAll() // clear logs
+ s.Require().Empty(s.logs.TakeAll())
+}
+
+func (s *loggingPayloadSuite) TestPing_LogsBothRequestAndResponse() {
+ _, err := s.Client.Ping(s.SimpleCtx(), testpb.GoodPing)
+ s.Require().NoError(err)
+ s.Require().Len(s.logs.All(), 2) // request and response
+ s.assertField("grpc.request.content", `{"value":"something","sleepTimeMs":9999}`, 1)
+ s.assertField("grpc.response.content", `{"value":"something"}`, 1)
+}
+
+func (s *loggingPayloadSuite) TestPingError_LogsError() {
+ _, err := s.Client.PingError(s.SimpleCtx(), &testpb.PingErrorRequest{Value: "something", ErrorCodeReturned: uint32(4)})
+ s.Require().Error(err)
+ s.Require().Len(s.logs.All(), 2) // request and response
+ s.assertField("grpc.request.content", `{"value":"something","errorCodeReturned":4}`, 1)
+ s.assertField("grpc.response.content", ``, 1)
+ s.assertField("grpc.response.error", "rpc error: code = DeadlineExceeded desc = Userspace error", 1)
+}
+
+func (s *loggingPayloadSuite) TestPingStream_LogsAllRequestsAndResponses() {
+ messagesExpected := 10
+ stream, err := s.Client.PingStream(s.SimpleCtx())
+ s.Require().NoError(err)
+
+ for range messagesExpected {
+ s.Require().NoError(stream.Send(testpb.GoodPingStream))
+ pong := &testpb.PingResponse{}
+ err := stream.RecvMsg(pong)
+ s.Require().NoError(err)
+ }
+ s.Require().NoError(stream.CloseSend())
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
+ defer cancel()
+ s.Require().NoError(waitUntil(200*time.Millisecond, ctx.Done(), func() error {
+ if len(s.logs.FilterFieldKey("grpc.request.error").All()) > 0 {
+ return nil
+ }
+ return fmt.Errorf("no EOF log yet")
+ }))
+ eof := s.logs.FilterFieldKey("grpc.request.error").All()
+ s.Len(eof, 1)
+ s.Equal(io.EOF.Error(), eof[0].ContextMap()["grpc.request.error"])
+ s.assertField("grpc.request.content", `{"value":"something","sleepTimeMs":9999}`, messagesExpected+1)
+ s.assertField("grpc.response.content", `{"value":"something"}`, messagesExpected)
+}
+
+func (s *loggingPayloadSuite) assertField(key, expectedValue string, expectedLineCount int) {
+ s.T().Helper()
+ filtered := s.logs.FilterFieldKey(key).All()
+ s.Require().Len(filtered, expectedLineCount)
+ actualValue, ok := filtered[0].ContextMap()[key].(string)
+ s.Require().True(ok)
+ s.Equal(expectedValue, actualValue)
+}
+
+// waitUntil executes f every interval seconds until timeout or no error is returned from f.
+func waitUntil(interval time.Duration, stopc <-chan struct{}, f func() error) error {
+ tick := time.NewTicker(interval)
+ defer tick.Stop()
+
+ var err error
+ for {
+ if err = f(); err == nil {
+ return nil
+ }
+ select {
+ case <-stopc:
+ return err
+ case <-tick.C:
+ }
+ }
+}
diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go
index 84adb28736d5..f04bb2e28a23 100644
--- a/server/etcdmain/help.go
+++ b/server/etcdmain/help.go
@@ -1,5 +1,4 @@
// Copyright 2015 The etcd Authors
-// Copyright 2015 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -42,6 +41,10 @@ var (
etcd --config-file
Path to the server configuration file. Note that if a configuration file is provided, other command line flags and environment variables will be ignored.
+ Duration-typed fields (for example --heartbeat-interval, --election-timeout,
+ --watch-progress-notify-interval) must be specified as integer nanoseconds
+ in the JSON/YAML file (e.g. 600000000000 for 10m); human-readable strings
+ such as "10m" are only accepted on the command line.
etcd gateway
Run the stateless pass-through etcd TCP connection forwarding proxy.
@@ -78,7 +81,7 @@ Member:
--memory-mlock
Enable to enforce etcd pages (in particular bbolt) to stay in RAM.
--quota-backend-bytes '0'
- Sets the maximum size (in bytes) that the etcd backend database may consume. Exceeding this triggers an alarm and puts etcd in read-only mode. Set to 0 to use the default 2GiB limit.
+ Raise alarms when backend size exceeds the given quota (0 defaults to low space quota).
--backend-bbolt-freelist-type 'map'
BackendFreelistType specifies the type of freelist that boltdb backend uses(array and map are supported types).
--backend-batch-interval ''
@@ -173,6 +176,7 @@ Clustering:
Supported values:
'not-yet' // Issues a warning if v2store have meaningful content (default in v3.5)
'write-only' // Custom v2 state is not allowed (default in v3.6)
+ 'write-only-skip-check' // Custom v2 state is not allowed similar to 'write-only', but bypass the v2 content check.
'write-only-drop-data' // Custom v2 state will get DELETED ! (planned default in v3.7)
'gone' // v2store is not maintained any longer. (planned to cleanup anything related to v2store in v3.8)
@@ -260,87 +264,41 @@ Logging:
--warning-unary-request-duration '300ms'
Set time duration after which a warning is logged if a unary request takes more than this duration.
-Experimental distributed tracing:
- --experimental-enable-distributed-tracing 'false'
- Enable experimental distributed tracing. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--enable-distributed-tracing' instead.
+Distributed tracing:
--enable-distributed-tracing 'false'
Enable distributed tracing.
- --experimental-distributed-tracing-address 'localhost:4317'
- Distributed tracing collector address. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--distributed-tracing-address' instead.
--distributed-tracing-address 'localhost:4317'
Distributed tracing collector address.
- --experimental-distributed-tracing-service-name 'etcd'
- Distributed tracing service name, must be same across all etcd instances. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--distributed-tracing-service-name' instead.
--distributed-tracing-service-name 'etcd'
Distributed tracing service name, must be same across all etcd instances.
- --experimental-distributed-tracing-instance-id ''
- Distributed tracing instance ID, must be unique per each etcd instance. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--distributed-tracing-instance-id' instead.
--distributed-tracing-instance-id ''
Distributed tracing instance ID, must be unique per each etcd instance.
- --experimental-distributed-tracing-sampling-rate '0'
- Number of samples to collect per million spans for distributed tracing. Disabled by default. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--distributed-tracing-sampling-rate' instead.
--distributed-tracing-sampling-rate '0'
Number of samples to collect per million spans for distributed tracing.
-Experimental feature:
- --experimental-initial-corrupt-check 'false'
- Enable to check data corruption before serving any client/peer traffic. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=InitialCorruptCheck=true' instead.
- --experimental-corrupt-check-time '0s'
- Duration of time between cluster corruption check passes. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--corrupt-check-time' instead.
+Features:
--corrupt-check-time '0s'
Duration of time between cluster corruption check passes.
- --experimental-compact-hash-check-enabled 'false'
- Enable leader to periodically check followers compaction hashes. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=CompactHashCheck=true' instead.
- --experimental-compact-hash-check-time '1m'
- Duration of time between leader checks followers compaction hashes. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--compact-hash-check-time' instead.
--compact-hash-check-time '1m'
Duration of time between leader checks followers compaction hashes.
- --experimental-enable-lease-checkpoint 'false'
- ExperimentalEnableLeaseCheckpoint enables primary lessor to persist lease remainingTTL to prevent indefinite auto-renewal of long lived leases. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=LeaseCheckpoint=true' instead.
- --experimental-compaction-batch-limit 1000
- ExperimentalCompactionBatchLimit sets the maximum revisions deleted in each compaction batch. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--compaction-batch-limit' instead.
--compaction-batch-limit 1000
CompactionBatchLimit sets the maximum revisions deleted in each compaction batch.
- --experimental-peer-skip-client-san-verification 'false'
- Skip verification of SAN field in client certificate for peer connections. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--peer-skip-client-san-verification' instead.
--peer-skip-client-san-verification 'false'
Skip verification of SAN field in client certificate for peer connections.
- --experimental-watch-progress-notify-interval '10m'
- Duration of periodical watch progress notification. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--watch-progress-notify-interval' instead.
--watch-progress-notify-interval '10m'
Duration of periodical watch progress notification.
- --experimental-warning-apply-duration '100ms'
- Warning is generated if requests take more than this duration. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--warning-apply-duration' instead.
--warning-apply-duration '100ms'
Warning is generated if requests take more than this duration.
- --experimental-txn-mode-write-with-shared-buffer 'true'
- Enable the write transaction to use a shared buffer in its readonly check operations. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=TxnModeWriteWithSharedBuffer=true' instead.
- --experimental-bootstrap-defrag-threshold-megabytes
- Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--bootstrap-defrag-threshold-megabytes' instead.
--bootstrap-defrag-threshold-megabytes
Enable the defrag during etcd server bootstrap on condition that it will free at least the provided threshold of disk space. Needs to be set to non-zero value to take effect.
- --experimental-warning-unary-request-duration '300ms'
- Set time duration after which a warning is generated if a unary request takes more than this duration. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--warning-unary-request-duration' instead.
--max-learners '1'
Set the max number of learner members allowed in the cluster membership.
- --experimental-compaction-sleep-interval
- Sets the sleep interval between each compaction batch. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--compaction-sleep-interval' instead.
--compaction-sleep-interval
Sets the sleep interval between each compaction batch.
- --experimental-downgrade-check-time
- Duration of time between two downgrade status checks. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--downgrade-check-time' instead.
--downgrade-check-time
Duration of time between two downgrade status checks.
- --experimental-enable-lease-checkpoint-persist 'false'
- Enable persisting remainingTTL to prevent indefinite auto-renewal of long lived leases. Always enabled in v3.6. Should be used to ensure smooth upgrade from v3.5 clusters with this feature enabled. Requires experimental-enable-lease-checkpoint to be enabled. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=LeaseCheckpointPersist=true' instead.
- --experimental-memory-mlock
- Enable to enforce etcd pages (in particular bbolt) to stay in RAM. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--memory-mlock' instead.
- --experimental-snapshot-catchup-entries
- Number of entries for a slow follower to catch up after compacting the raft storage entries. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--snapshot-catchup-entries' instead.
--snapshot-catchup-entries
Number of entries for a slow follower to catch up after compacting the raft storage entries.
- --experimental-stop-grpc-service-on-defrag
- Enable etcd gRPC service to stop serving client requests on defragmentation. Deprecated in v3.6 and will be decommissioned in v3.7. Use '--feature-gates=StopGRPCServiceOnDefrag=true' instead.
Unsafe feature:
--force-new-cluster 'false'
diff --git a/server/etcdserver/adapters.go b/server/etcdserver/adapters.go
index bc9790bc2e68..ec4120042931 100644
--- a/server/etcdserver/adapters.go
+++ b/server/etcdserver/adapters.go
@@ -45,19 +45,15 @@ func (s *serverVersionAdapter) UpdateClusterVersion(version string) {
s.GoAttach(func() { s.updateClusterVersionV3(version) })
}
-func (s *serverVersionAdapter) LinearizableReadNotify(ctx context.Context) error {
- return s.linearizableReadNotify(ctx)
-}
-
func (s *serverVersionAdapter) DowngradeEnable(ctx context.Context, targetVersion *semver.Version) error {
raftRequest := membershippb.DowngradeInfoSetRequest{Enabled: true, Ver: targetVersion.String()}
- _, err := s.raftRequest(ctx, pb.InternalRaftRequest{DowngradeInfoSet: &raftRequest})
+ _, err := s.raftRequest(ctx, &pb.InternalRaftRequest{DowngradeInfoSet: &raftRequest})
return err
}
func (s *serverVersionAdapter) DowngradeCancel(ctx context.Context) error {
raftRequest := membershippb.DowngradeInfoSetRequest{Enabled: false}
- _, err := s.raftRequest(ctx, pb.InternalRaftRequest{DowngradeInfoSet: &raftRequest})
+ _, err := s.raftRequest(ctx, &pb.InternalRaftRequest{DowngradeInfoSet: &raftRequest})
return err
}
diff --git a/server/etcdserver/api/capability.go b/server/etcdserver/api/capability.go
index cf535ec4efaa..3efe6c74d704 100644
--- a/server/etcdserver/api/capability.go
+++ b/server/etcdserver/api/capability.go
@@ -41,6 +41,7 @@ var (
"3.4.0": {AuthCapability: true, V3rpcCapability: true},
"3.5.0": {AuthCapability: true, V3rpcCapability: true},
"3.6.0": {AuthCapability: true, V3rpcCapability: true},
+ "3.7.0": {AuthCapability: true, V3rpcCapability: true},
}
enableMapMu sync.RWMutex
diff --git a/server/etcdserver/api/cluster.go b/server/etcdserver/api/cluster.go
index f05997da52a0..cf483089db90 100644
--- a/server/etcdserver/api/cluster.go
+++ b/server/etcdserver/api/cluster.go
@@ -15,10 +15,10 @@
package api
import (
+ "github.com/coreos/go-semver/semver"
+
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
-
- "github.com/coreos/go-semver/semver"
)
// Cluster is an interface representing a collection of members in one etcd cluster.
diff --git a/server/etcdserver/api/etcdhttp/health.go b/server/etcdserver/api/etcdhttp/health.go
index 26ed4cab1683..1a6fa51be301 100644
--- a/server/etcdserver/api/etcdhttp/health.go
+++ b/server/etcdserver/api/etcdhttp/health.go
@@ -73,7 +73,7 @@ func HandleHealth(lg *zap.Logger, mux *http.ServeMux, srv ServerHealth) {
}
// NewHealthHandler handles '/health' requests.
-func NewHealthHandler(lg *zap.Logger, hfunc func(ctx context.Context, excludedAlarms StringSet, Serializable bool) Health) http.HandlerFunc {
+func NewHealthHandler(lg *zap.Logger, hfunc func(ctx context.Context, excludedAlarms StringSet, serializable bool) Health) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.Header().Set("Allow", http.MethodGet)
diff --git a/server/etcdserver/api/etcdhttp/health_test.go b/server/etcdserver/api/etcdhttp/health_test.go
index 06226e136f80..69822bc93a10 100644
--- a/server/etcdserver/api/etcdhttp/health_test.go
+++ b/server/etcdserver/api/etcdhttp/health_test.go
@@ -26,8 +26,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap/zaptest"
- "go.etcd.io/raft/v3"
-
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/testutil"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -35,6 +33,7 @@ import (
"go.etcd.io/etcd/server/v3/config"
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
"go.etcd.io/etcd/server/v3/storage/schema"
+ "go.etcd.io/raft/v3"
)
type fakeHealthServer struct {
diff --git a/server/etcdserver/api/etcdhttp/types/errors_test.go b/server/etcdserver/api/etcdhttp/types/errors_test.go
index 287950dc13e6..f9ef20ca8c99 100644
--- a/server/etcdserver/api/etcdhttp/types/errors_test.go
+++ b/server/etcdserver/api/etcdhttp/types/errors_test.go
@@ -38,8 +38,8 @@ func TestHTTPErrorWriteTo(t *testing.T) {
t.Errorf("HTTP status code %d, want %d", rr.Code, wcode)
}
- if !reflect.DeepEqual(wheader, rr.HeaderMap) {
- t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader)
+ if !reflect.DeepEqual(wheader, rr.HeaderMap) { //nolint:staticcheck // TODO: remove for a supported version
+ t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader) //nolint:staticcheck // TODO: remove for a supported version
}
gbody := rr.Body.String()
diff --git a/server/etcdserver/api/etcdhttp/version_test.go b/server/etcdserver/api/etcdhttp/version_test.go
index 3da19bb791d4..2418c1a4c2ce 100644
--- a/server/etcdserver/api/etcdhttp/version_test.go
+++ b/server/etcdserver/api/etcdhttp/version_test.go
@@ -45,7 +45,7 @@ func TestServeVersion(t *testing.T) {
if g := rw.Body.String(); g != string(w) {
t.Fatalf("body = %q, want %q", g, string(w))
}
- if ct := rw.HeaderMap.Get("Content-Type"); ct != "application/json" {
+ if ct := rw.HeaderMap.Get("Content-Type"); ct != "application/json" { //nolint:staticcheck // TODO: remove for a supported version
t.Errorf("contet-type header = %s, want %s", ct, "application/json")
}
}
diff --git a/server/etcdserver/api/membership/cluster.go b/server/etcdserver/api/membership/cluster.go
index 299e61374cea..66120cfe6826 100644
--- a/server/etcdserver/api/membership/cluster.go
+++ b/server/etcdserver/api/membership/cluster.go
@@ -46,8 +46,7 @@ type RaftCluster struct {
localID types.ID
cid types.ID
- v2store v2store.Store
- be MembershipBackend
+ be MembershipBackend
sync.Mutex // guards the fields below
version *semver.Version
@@ -245,8 +244,6 @@ func (c *RaftCluster) SetID(localID, cid types.ID) {
c.buildMembershipMetric()
}
-func (c *RaftCluster) SetStore(st v2store.Store) { c.v2store = st }
-
func (c *RaftCluster) SetBackend(be MembershipBackend) {
c.be = be
c.be.MustCreateBackendBuckets()
@@ -257,17 +254,9 @@ func (c *RaftCluster) SetVersionChangedNotifier(n *notify.Notifier) {
}
func (c *RaftCluster) UnsafeLoad() {
- if c.be != nil {
- c.version = c.be.ClusterVersionFromBackend()
- c.members, c.removed = c.be.MustReadMembersFromBackend()
- } else {
- c.version = clusterVersionFromStore(c.lg, c.v2store)
- c.members, c.removed = membersFromStore(c.lg, c.v2store)
- }
-
- if c.be != nil {
- c.downgradeInfo = c.be.DowngradeInfoFromBackend()
- }
+ c.version = c.be.ClusterVersionFromBackend()
+ c.members, c.removed = c.be.MustReadMembersFromBackend()
+ c.downgradeInfo = c.be.DowngradeInfoFromBackend()
}
func (c *RaftCluster) Recover(onSet func(*zap.Logger, *semver.Version)) {
@@ -308,26 +297,24 @@ func (c *RaftCluster) Recover(onSet func(*zap.Logger, *semver.Version)) {
"set cluster version from store",
zap.String("cluster-version", version.Cluster(c.version.String())),
)
+ ClusterVersionMetrics.With(prometheus.Labels{"cluster_version": version.Cluster(c.version.String())}).Set(1)
}
}
// ValidateConfigurationChange takes a proposed ConfChange and
// ensures that it is still valid.
-func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange, shouldApplyV3 ShouldApplyV3) error {
- var membersMap map[types.ID]*Member
- var removedMap map[types.ID]bool
-
- if shouldApplyV3 {
- membersMap, removedMap = c.be.MustReadMembersFromBackend()
- } else {
- membersMap, removedMap = membersFromStore(c.lg, c.v2store)
+func (c *RaftCluster) ValidateConfigurationChange(cc *raftpb.ConfChange, shouldApplyV3 ShouldApplyV3) error {
+ if !shouldApplyV3 {
+ return nil
}
- id := types.ID(cc.NodeID)
+ membersMap, removedMap := c.be.MustReadMembersFromBackend()
+
+ id := types.ID(cc.GetNodeId())
if removedMap[id] {
return ErrIDRemoved
}
- switch cc.Type {
+ switch cc.GetType() {
case raftpb.ConfChangeAddNode, raftpb.ConfChangeAddLearnerNode:
confChangeContext := new(ConfigChangeContext)
if err := json.Unmarshal(cc.Context, confChangeContext); err != nil {
@@ -360,7 +347,7 @@ func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange, shouldAp
}
}
- if confChangeContext.Member.RaftAttributes.IsLearner && cc.Type == raftpb.ConfChangeAddLearnerNode { // the new member is a learner
+ if confChangeContext.Member.RaftAttributes.IsLearner && cc.GetType() == raftpb.ConfChangeAddLearnerNode { // the new member is a learner
scaleUpLearners := true
if err := ValidateMaxLearnerConfig(c.maxLearners, members, scaleUpLearners); err != nil {
return err
@@ -396,7 +383,7 @@ func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange, shouldAp
}
default:
- c.lg.Panic("unknown ConfChange type", zap.String("type", cc.Type.String()))
+ c.lg.Panic("unknown ConfChange type", zap.String("type", cc.GetType().String()))
}
return nil
}
@@ -407,15 +394,12 @@ func (c *RaftCluster) ValidateConfigurationChange(cc raftpb.ConfChange, shouldAp
func (c *RaftCluster) AddMember(m *Member, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
- if c.v2store != nil {
- mustSaveMemberToStore(c.lg, c.v2store, m)
- }
if m.ID == c.localID {
setIsLearnerMetric(m)
}
- if c.be != nil && shouldApplyV3 {
+ if shouldApplyV3 {
c.be.MustSaveMemberToBackend(m)
c.members[m.ID] = m
@@ -445,10 +429,7 @@ func (c *RaftCluster) AddMember(m *Member, shouldApplyV3 ShouldApplyV3) {
func (c *RaftCluster) RemoveMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
- if c.v2store != nil {
- mustDeleteMemberFromStore(c.lg, c.v2store, id)
- }
- if c.be != nil && shouldApplyV3 {
+ if shouldApplyV3 {
c.be.MustDeleteMemberFromBackend(id)
m, ok := c.members[id]
@@ -489,10 +470,7 @@ func (c *RaftCluster) UpdateAttributes(id types.ID, attr Attributes, shouldApply
if m, ok := c.members[id]; ok {
m.Attributes = attr
- if c.v2store != nil {
- mustUpdateMemberAttrInStore(c.lg, c.v2store, m)
- }
- if c.be != nil && shouldApplyV3 {
+ if shouldApplyV3 {
c.be.MustSaveMemberToBackend(m)
}
return
@@ -521,28 +499,12 @@ func (c *RaftCluster) PromoteMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
- if c.v2store != nil {
- membersMap, _ := membersFromStore(c.lg, c.v2store)
- if _, ok := membersMap[id]; ok {
- m := *(membersMap[id])
- m.RaftAttributes.IsLearner = false
- mustUpdateMemberInStore(c.lg, c.v2store, &m)
- } else {
- c.lg.Info("Skipped promoting non-existent member in v2store",
- zap.String("cluster-id", c.cid.String()),
- zap.String("local-member-id", c.localID.String()),
- zap.String("promoted-member-id", id.String()),
- )
- }
- }
-
if id == c.localID {
isLearner.Set(0)
}
- if c.be != nil {
- m := c.members[id]
- if shouldApplyV3 {
+ if shouldApplyV3 {
+ if m, ok := c.members[id]; ok {
m.RaftAttributes.IsLearner = false
c.updateMembershipMetric(id, true)
c.be.MustSaveMemberToBackend(m)
@@ -554,89 +516,46 @@ func (c *RaftCluster) PromoteMember(id types.ID, shouldApplyV3 ShouldApplyV3) {
zap.String("promoted-member-id", id.String()),
)
} else {
- // Workaround the issues which have already been affected by
- // https://github.com/etcd-io/etcd/issues/19557. The learner
- // promotion request had been applied to v3store, but not saved
- // to v2snapshot yet when in 3.5. Once upgrading to 3.6, the
- // patch here ensure the issue can be automatically fixed.
- if m == nil {
- c.lg.Info(
- "Skipped forcibly promoting non-existent member in v3store",
- zap.String("cluster-id", c.cid.String()),
- zap.String("local-member-id", c.localID.String()),
- zap.String("promoted-member-id", id.String()),
- )
- } else if m.IsLearner {
- m.RaftAttributes.IsLearner = false
- c.lg.Info("Forcibly apply member promotion request in v3store", zap.String("member", fmt.Sprintf("%+v", *m)))
- c.be.MustHackySaveMemberToBackend(m)
- } else {
- c.lg.Info(
- "ignore already promoted member in v3store",
- zap.String("cluster-id", c.cid.String()),
- zap.String("local-member-id", c.localID.String()),
- zap.String("promoted-member-id", id.String()),
- )
- }
+ c.lg.Info(
+ "ignore promoting non-existent member",
+ zap.String("cluster-id", c.cid.String()),
+ zap.String("local-member-id", c.localID.String()),
+ zap.String("promoted-member-id", id.String()),
+ )
}
} else {
c.lg.Info(
- "ignore already promoted member due to backend being nil",
+ "ignore already promoted member",
zap.String("cluster-id", c.cid.String()),
zap.String("local-member-id", c.localID.String()),
- zap.String("promoted-member-id", id.String()),
)
}
}
-// SyncLearnerPromotionIfNeeded provides a workaround solution to fix the issues
-// which have already been affected by https://github.com/etcd-io/etcd/issues/19557.
-func (c *RaftCluster) SyncLearnerPromotionIfNeeded() {
- c.Lock()
- defer c.Unlock()
-
- v2Members, _ := membersFromStore(c.lg, c.v2store)
- v3Members, _ := c.be.MustReadMembersFromBackend()
-
- for id, v3Member := range v3Members {
- v2Member, ok := v2Members[id]
- if !ok {
- // This isn't an error. The conf change on the member hasn't been saved to the v2 snapshot yet.
- c.lg.Info("Detected member only in v3store but missing in v2store", zap.String("member", fmt.Sprintf("%+v", *v3Member)))
- continue
- }
-
- if !v2Member.IsLearner && v3Member.IsLearner {
- syncedV3Member := v3Member.Clone()
- syncedV3Member.IsLearner = false
- c.lg.Warn("Syncing member in v3store", zap.String("member", fmt.Sprintf("%+v", *syncedV3Member)))
- c.be.MustHackySaveMemberToBackend(syncedV3Member)
- }
- }
-}
-
func (c *RaftCluster) UpdateRaftAttributes(id types.ID, raftAttr RaftAttributes, shouldApplyV3 ShouldApplyV3) {
c.Lock()
defer c.Unlock()
- if c.v2store != nil {
- if _, ok := c.members[id]; ok {
- m := *(c.members[id])
- m.RaftAttributes = raftAttr
- mustUpdateMemberInStore(c.lg, c.v2store, &m)
- } else {
- c.lg.Info("Skipped updating non-existent member in v2store",
- zap.String("cluster-id", c.cid.String()),
- zap.String("local-member-id", c.localID.String()),
- zap.String("updated-remote-peer-id", id.String()),
- zap.Strings("updated-remote-peer-urls", raftAttr.PeerURLs),
- zap.Bool("updated-remote-peer-is-learner", raftAttr.IsLearner),
- )
- }
+ var (
+ m *Member
+ ok bool
+ )
+ if m, ok = c.members[id]; !ok {
+ c.lg.Info("Skipped updating non-existent member in v2store",
+ zap.String("cluster-id", c.cid.String()),
+ zap.String("local-member-id", c.localID.String()),
+ zap.String("updated-remote-peer-id", id.String()),
+ zap.Strings("updated-remote-peer-urls", raftAttr.PeerURLs),
+ zap.Bool("updated-remote-peer-is-learner", raftAttr.IsLearner),
+ zap.Bool("should-apply-v3", bool(shouldApplyV3)),
+ )
+ return
}
- if c.be != nil && shouldApplyV3 {
- c.members[id].RaftAttributes = raftAttr
- c.be.MustSaveMemberToBackend(c.members[id])
+ // `MemberUpdateRequest` only supports updating PeerURLs.
+ m.RaftAttributes.PeerURLs = raftAttr.PeerURLs
+
+ if shouldApplyV3 {
+ c.be.MustSaveMemberToBackend(m)
c.lg.Info(
"updated member",
@@ -690,10 +609,8 @@ func (c *RaftCluster) SetVersion(ver *semver.Version, onSet func(*zap.Logger, *s
c.version = ver
sv := semver.Must(semver.NewVersion(version.Version))
serverversion.MustDetectDowngrade(c.lg, sv, c.version)
- if c.v2store != nil {
- mustSaveClusterVersionToStore(c.lg, c.v2store, ver)
- }
- if c.be != nil && shouldApplyV3 {
+
+ if shouldApplyV3 {
c.be.MustSaveClusterVersionToBackend(ver)
}
if oldVer != nil {
@@ -795,15 +712,7 @@ func (c *RaftCluster) IsReadyToPromoteMember(id uint64) bool {
return true
}
-func (c *RaftCluster) MembersFromBackend() (map[types.ID]*Member, map[types.ID]bool) {
- return c.be.MustReadMembersFromBackend()
-}
-
-func (c *RaftCluster) MembersFromStore() (map[types.ID]*Member, map[types.ID]bool) {
- return membersFromStore(c.lg, c.v2store)
-}
-
-func membersFromStore(lg *zap.Logger, st v2store.Store) (map[types.ID]*Member, map[types.ID]bool) {
+func MembersFromStore(lg *zap.Logger, st v2store.Store) (map[types.ID]*Member, map[types.ID]bool) {
members := make(map[types.ID]*Member)
removed := make(map[types.ID]bool)
e, err := st.Get(StoreMembersPrefix, true, true)
@@ -903,7 +812,7 @@ func (c *RaftCluster) SetDowngradeInfo(d *serverversion.DowngradeInfo, shouldApp
c.Lock()
defer c.Unlock()
- if c.be != nil && shouldApplyV3 {
+ if shouldApplyV3 {
c.be.MustSaveDowngradeToBackend(d)
}
diff --git a/server/etcdserver/api/membership/cluster_test.go b/server/etcdserver/api/membership/cluster_test.go
index 62f0619f2aac..b46b386d0be3 100644
--- a/server/etcdserver/api/membership/cluster_test.go
+++ b/server/etcdserver/api/membership/cluster_test.go
@@ -18,7 +18,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "path"
"reflect"
"testing"
@@ -26,10 +25,8 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
- "go.etcd.io/etcd/client/pkg/v3/testutil"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
- "go.etcd.io/etcd/server/v3/mock/mockstore"
"go.etcd.io/raft/v3/raftpb"
)
@@ -270,7 +267,7 @@ func TestClusterValidateAndAssignIDs(t *testing.T) {
lcl := newTestCluster(t, tt.clmembs)
ecl := newTestCluster(t, tt.membs)
if err := ValidateClusterAndAssignIDs(zaptest.NewLogger(t), lcl, ecl); err != nil {
- t.Errorf("#%d: unexpect update error: %v", i, err)
+ t.Errorf("#%d: unexpected update error: %v", i, err)
}
if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
@@ -279,18 +276,9 @@ func TestClusterValidateAndAssignIDs(t *testing.T) {
}
func TestClusterValidateConfigurationChangeV3(t *testing.T) {
- testClusterValidateConfigurationChange(t, true)
-}
-
-func TestClusterValidateConfigurationChangeV2(t *testing.T) {
- testClusterValidateConfigurationChange(t, false)
-}
-
-func testClusterValidateConfigurationChange(t *testing.T, shouldApplyV3 ShouldApplyV3) {
cl := NewCluster(zaptest.NewLogger(t), WithMaxLearners(1))
be := newMembershipBackend()
cl.SetBackend(be)
- cl.SetStore(v2store.New())
for i := 1; i <= 4; i++ {
var isLearner bool
if i == 1 {
@@ -353,121 +341,121 @@ func testClusterValidateConfigurationChange(t *testing.T, shouldApplyV3 ShouldAp
t.Fatal(err)
}
tests := []struct {
- cc raftpb.ConfChange
+ cc *raftpb.ConfChange
werr error
}{
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: 3,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(3)),
},
nil,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 4,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(4)),
},
ErrIDRemoved,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: 4,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(4)),
},
ErrIDRemoved,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 1,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(1)),
Context: ctx1,
},
ErrIDExists,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 5,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(5)),
Context: ctx,
},
ErrPeerURLexists,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: 5,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(5)),
},
ErrIDNotFound,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 5,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(5)),
Context: ctx5,
},
nil,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: 5,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(5)),
Context: ctx,
},
ErrIDNotFound,
},
// try to change the peer url of 2 to the peer url of 3
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: 2,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(2)),
Context: ctx2to3,
},
ErrPeerURLexists,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: 2,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(2)),
Context: ctx2to5,
},
nil,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 3,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(3)),
Context: ctx3,
},
ErrMemberNotLearner,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 6,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(6)),
Context: ctx6,
},
ErrIDNotFound,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddLearnerNode,
- NodeID: 7,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddLearnerNode.Enum(),
+ NodeId: new(uint64(7)),
Context: ctx7,
},
ErrTooManyLearners,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 1,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(1)),
Context: ctx8,
},
nil,
},
}
for i, tt := range tests {
- err := cl.ValidateConfigurationChange(tt.cc, shouldApplyV3)
+ err := cl.ValidateConfigurationChange(tt.cc, true)
if !errors.Is(err, tt.werr) {
t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
}
@@ -489,7 +477,6 @@ func TestClusterGenID(t *testing.T) {
}
previd := cs.ID()
- cs.SetStore(mockstore.NewNop())
cs.AddMember(newTestMember(3, nil, "", nil), true)
cs.genID()
if cs.ID() == previd {
@@ -530,49 +517,35 @@ func TestNodeToMemberBad(t *testing.T) {
}
func TestClusterAddMember(t *testing.T) {
- st := mockstore.NewRecorder()
- c := newTestCluster(t, nil)
- c.SetStore(st)
- c.AddMember(newTestMember(1, nil, "node1", nil), true)
+ t.Run("V3", func(t *testing.T) {
+ c := newTestCluster(t, nil)
+ c.AddMember(newTestMember(1, nil, "node1", nil), true)
- wactions := []testutil.Action{
- {
- Name: "Create",
- Params: []any{
- path.Join(StoreMembersPrefix, "1", "raftAttributes"),
- false,
- `{"peerURLs":null}`,
- false,
- v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
- },
- },
- }
- if g := st.Action(); !reflect.DeepEqual(g, wactions) {
- t.Errorf("actions = %v, want %v", g, wactions)
- }
+ members, _ := c.be.MustReadMembersFromBackend()
+ if len(members) != 1 {
+ t.Errorf("members = %v, want 1 member", members)
+ }
+ if _, ok := members[types.ID(1)]; !ok {
+ t.Errorf("member 1 not found")
+ }
+ })
}
func TestClusterAddMemberAsLearner(t *testing.T) {
- st := mockstore.NewRecorder()
- c := newTestCluster(t, nil)
- c.SetStore(st)
- c.AddMember(newTestMemberAsLearner(1, []string{}, "node1", []string{"http://node1"}), true)
+ t.Run("V3", func(t *testing.T) {
+ c := newTestCluster(t, nil)
+ c.AddMember(newTestMemberAsLearner(1, []string{}, "node1", []string{"http://node1"}), true)
- wactions := []testutil.Action{
- {
- Name: "Create",
- Params: []any{
- path.Join(StoreMembersPrefix, "1", "raftAttributes"),
- false,
- `{"peerURLs":[],"isLearner":true}`,
- false,
- v2store.TTLOptionSet{ExpireTime: v2store.Permanent},
- },
- },
- }
- if g := st.Action(); !reflect.DeepEqual(g, wactions) {
- t.Errorf("actions = %v, want %v", g, wactions)
- }
+ members, _ := c.be.MustReadMembersFromBackend()
+ if len(members) != 1 {
+ t.Errorf("members = %v, want 1 member", members)
+ }
+ if m, ok := members[types.ID(1)]; !ok {
+ t.Errorf("member 1 not found")
+ } else if !m.IsLearner {
+ t.Errorf("member 1 is not learner")
+ }
+ })
}
func TestClusterMembers(t *testing.T) {
@@ -596,18 +569,19 @@ func TestClusterMembers(t *testing.T) {
}
func TestClusterRemoveMember(t *testing.T) {
- st := mockstore.NewRecorder()
- c := newTestCluster(t, nil)
- c.SetStore(st)
- c.RemoveMember(1, true)
-
- wactions := []testutil.Action{
- {Name: "Delete", Params: []any{MemberStoreKey(1), true, true}},
- {Name: "Create", Params: []any{RemovedMemberStoreKey(1), false, "", false, v2store.TTLOptionSet{ExpireTime: v2store.Permanent}}},
- }
- if !reflect.DeepEqual(st.Action(), wactions) {
- t.Errorf("actions = %v, want %v", st.Action(), wactions)
- }
+ t.Run("V3", func(t *testing.T) {
+ c := newTestCluster(t, nil)
+ c.AddMember(newTestMember(1, nil, "node1", nil), true)
+ c.RemoveMember(1, true)
+
+ members, removed := c.be.MustReadMembersFromBackend()
+ if len(members) != 0 {
+ t.Errorf("members = %v, want 0 member", members)
+ }
+ if !removed[types.ID(1)] {
+ t.Errorf("member 1 not removed")
+ }
+ })
}
func TestClusterUpdateAttributes(t *testing.T) {
@@ -636,13 +610,34 @@ func TestClusterUpdateAttributes(t *testing.T) {
},
}
for i, tt := range tests {
- c := newTestCluster(t, tt.mems)
- c.removed = tt.removed
+ t.Run(fmt.Sprintf("V3-%d", i), func(t *testing.T) {
+ c := newTestCluster(t, tt.mems)
+ for id := range tt.removed {
+ c.be.MustDeleteMemberFromBackend(id)
+ }
+ c.removed = tt.removed
- c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs}, true)
- if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
- t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
- }
+ c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs}, true)
+
+ // Verify in-memory state
+ if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
+ t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
+ }
+
+ bmembers, _ := c.be.MustReadMembersFromBackend()
+ if len(tt.wmems) > 0 {
+ if m, ok := bmembers[types.ID(1)]; !ok {
+ t.Errorf("member 1 not found in backend")
+ } else {
+ if m.Name != name {
+ t.Errorf("member 1 name = %s, want %s", m.Name, name)
+ }
+ if !reflect.DeepEqual(m.ClientURLs, clientURLs) {
+ t.Errorf("member 1 clientURLs = %v, want %v", m.ClientURLs, clientURLs)
+ }
+ }
+ }
+ })
}
}
@@ -661,10 +656,15 @@ func TestNodeToMember(t *testing.T) {
}
}
-func newTestCluster(t testing.TB, membs []*Member) *RaftCluster {
- c := &RaftCluster{lg: zaptest.NewLogger(t), members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
+func newTestCluster(tb testing.TB, membs []*Member) *RaftCluster {
+ c := &RaftCluster{
+ lg: zaptest.NewLogger(tb),
+ members: make(map[types.ID]*Member),
+ removed: make(map[types.ID]bool),
+ be: newMembershipBackend(),
+ }
for _, m := range membs {
- c.members[m.ID] = m
+ c.AddMember(m, true)
}
return c
}
@@ -1040,15 +1040,14 @@ func TestPromoteMember(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- c := newTestCluster(t, tc.members)
- st := v2store.New("/0", "/1")
- c.Store(st)
- c.SetStore(st)
+ t.Run("V3", func(t *testing.T) {
+ c := newTestCluster(t, tc.members)
- c.PromoteMember(tc.promoteID, false)
+ c.PromoteMember(tc.promoteID, true)
- mst, _ := membersFromStore(c.lg, st)
- require.Equal(t, tc.wantMembers, mst)
+ mst, _ := c.be.MustReadMembersFromBackend()
+ require.Equal(t, tc.wantMembers, mst)
+ })
})
}
}
@@ -1064,7 +1063,7 @@ func TestUpdateRaftAttributes(t *testing.T) {
wantMembers map[types.ID]*Member
}{
{
- name: "update an existing member",
+ name: "update an existing voting member",
members: []*Member{
newTestMember(1, oldPeerURLs, "1", clientURLs),
newTestMember(2, oldPeerURLs, "2", clientURLs),
@@ -1075,6 +1074,18 @@ func TestUpdateRaftAttributes(t *testing.T) {
2: newTestMember(2, newPeerURLs, "2", clientURLs),
},
},
+ {
+ name: "update an existing learner member",
+ members: []*Member{
+ newTestMember(1, oldPeerURLs, "1", clientURLs),
+ newTestMemberAsLearner(2, oldPeerURLs, "2", clientURLs),
+ },
+ updateMemberID: 2,
+ wantMembers: map[types.ID]*Member{
+ 1: newTestMember(1, oldPeerURLs, "1", clientURLs),
+ 2: newTestMemberAsLearner(2, newPeerURLs, "2", clientURLs),
+ },
+ },
{
name: "update a non-exist member",
members: []*Member{
@@ -1090,16 +1101,14 @@ func TestUpdateRaftAttributes(t *testing.T) {
}
for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
+ t.Run("V3", func(t *testing.T) {
c := newTestCluster(t, tc.members)
- st := v2store.New("/0", "/1")
- c.Store(st)
- c.SetStore(st)
- c.UpdateRaftAttributes(tc.updateMemberID, RaftAttributes{PeerURLs: newPeerURLs}, false)
+ c.UpdateRaftAttributes(tc.updateMemberID, RaftAttributes{PeerURLs: newPeerURLs}, true)
- mst, _ := membersFromStore(c.lg, st)
+ mst, _ := c.be.MustReadMembersFromBackend()
require.Equal(t, tc.wantMembers, mst)
+ require.Equal(t, tc.wantMembers, c.members)
})
}
}
@@ -1159,7 +1168,7 @@ func TestClusterStore(t *testing.T) {
c.Store(st)
// Verify that the members are properly stored
- mst, rst := membersFromStore(c.lg, st)
+ mst, rst := MembersFromStore(c.lg, st)
for _, mem := range tt.mems {
assert.Equal(t, mem, mst[mem.ID])
}
diff --git a/server/etcdserver/api/membership/membership_test.go b/server/etcdserver/api/membership/membership_test.go
index 4921ffa42ea6..3eb0d066af03 100644
--- a/server/etcdserver/api/membership/membership_test.go
+++ b/server/etcdserver/api/membership/membership_test.go
@@ -34,10 +34,6 @@ func TestAddRemoveMember(t *testing.T) {
c.AddMember(newTestMember(18, nil, "node18", nil), true)
c.RemoveMember(18, true)
- // Skipping removal of already removed member
- c.RemoveMember(17, true)
- c.RemoveMember(18, true)
-
c.AddMember(newTestMember(19, nil, "node19", nil), true)
// Recover from backend
diff --git a/server/etcdserver/api/membership/storev2.go b/server/etcdserver/api/membership/storev2.go
index 051150520b22..fd90f785a550 100644
--- a/server/etcdserver/api/membership/storev2.go
+++ b/server/etcdserver/api/membership/storev2.go
@@ -115,7 +115,7 @@ func IsMetaStoreOnly(store v2store.Store) (bool, error) {
}
func verifyNoMembersInStore(lg *zap.Logger, s v2store.Store) {
- members, removed := membersFromStore(lg, s)
+ members, removed := MembersFromStore(lg, s)
if len(members) != 0 || len(removed) != 0 {
lg.Panic("store has membership info")
}
@@ -136,18 +136,6 @@ func mustSaveMemberToStore(lg *zap.Logger, s v2store.Store, m *Member) {
}
}
-func mustDeleteMemberFromStore(lg *zap.Logger, s v2store.Store, id types.ID) {
- if _, err := s.Delete(MemberStoreKey(id), true, true); err != nil {
- lg.Panic(
- "failed to delete member from store",
- zap.String("path", MemberStoreKey(id)),
- zap.Error(err),
- )
- }
-
- mustAddToRemovedMembersInStore(lg, s, id)
-}
-
func mustAddToRemovedMembersInStore(lg *zap.Logger, s v2store.Store, id types.ID) {
if _, err := s.Create(RemovedMemberStoreKey(id), false, "", false, v2store.TTLOptionSet{ExpireTime: v2store.Permanent}); err != nil {
lg.Panic(
@@ -158,21 +146,6 @@ func mustAddToRemovedMembersInStore(lg *zap.Logger, s v2store.Store, id types.ID
}
}
-func mustUpdateMemberInStore(lg *zap.Logger, s v2store.Store, m *Member) {
- b, err := json.Marshal(m.RaftAttributes)
- if err != nil {
- lg.Panic("failed to marshal raftAttributes", zap.Error(err))
- }
- p := path.Join(MemberStoreKey(m.ID), raftAttributesSuffix)
- if _, err := s.Update(p, string(b), v2store.TTLOptionSet{ExpireTime: v2store.Permanent}); err != nil {
- lg.Panic(
- "failed to update raftAttributes",
- zap.String("path", p),
- zap.Error(err),
- )
- }
-}
-
func mustUpdateMemberAttrInStore(lg *zap.Logger, s v2store.Store, m *Member) {
b, err := json.Marshal(m.Attributes)
if err != nil {
@@ -241,18 +214,3 @@ func MemberStoreKey(id types.ID) string {
func MemberAttributesStorePath(id types.ID) string {
return path.Join(MemberStoreKey(id), attributesSuffix)
}
-
-func clusterVersionFromStore(lg *zap.Logger, st v2store.Store) *semver.Version {
- e, err := st.Get(path.Join(storePrefix, "version"), false, false)
- if err != nil {
- if isKeyNotFound(err) {
- return nil
- }
- lg.Panic(
- "failed to get cluster version from store",
- zap.String("path", path.Join(storePrefix, "version")),
- zap.Error(err),
- )
- }
- return semver.Must(semver.NewVersion(*e.Node.Value))
-}
diff --git a/server/etcdserver/api/rafthttp/coder.go b/server/etcdserver/api/rafthttp/coder.go
index 977442998966..b737b81d4ce1 100644
--- a/server/etcdserver/api/rafthttp/coder.go
+++ b/server/etcdserver/api/rafthttp/coder.go
@@ -23,5 +23,5 @@ type encoder interface {
type decoder interface {
// decode decodes the message from an input stream.
- decode() (raftpb.Message, error)
+ decode() (*raftpb.Message, error)
}
diff --git a/server/etcdserver/api/rafthttp/functional_test.go b/server/etcdserver/api/rafthttp/functional_test.go
index cf507d2773e9..99c9f9eb5e1a 100644
--- a/server/etcdserver/api/rafthttp/functional_test.go
+++ b/server/etcdserver/api/rafthttp/functional_test.go
@@ -17,11 +17,11 @@ package rafthttp
import (
"context"
"net/http/httptest"
- "reflect"
"testing"
"time"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/types"
stats "go.etcd.io/etcd/server/v3/etcdserver/api/v2stats"
@@ -43,7 +43,7 @@ func TestSendMessage(t *testing.T) {
defer srv.Close()
// member 2
- recvc := make(chan raftpb.Message, 1)
+ recvc := make(chan *raftpb.Message, 1)
p := &fakeRaft{recvc: recvc}
tr2 := &Transport{
ID: types.ID(2),
@@ -65,21 +65,21 @@ func TestSendMessage(t *testing.T) {
}
data := []byte("some data")
- tests := []raftpb.Message{
+ tests := []*raftpb.Message{
// these messages are set to send to itself, which facilitates testing.
- {Type: raftpb.MsgProp, From: 1, To: 2, Entries: []raftpb.Entry{{Data: data}}},
- {Type: raftpb.MsgApp, From: 1, To: 2, Term: 1, Index: 3, LogTerm: 0, Entries: []raftpb.Entry{{Index: 4, Term: 1, Data: data}}, Commit: 3},
- {Type: raftpb.MsgAppResp, From: 1, To: 2, Term: 1, Index: 3},
- {Type: raftpb.MsgVote, From: 1, To: 2, Term: 1, Index: 3, LogTerm: 0},
- {Type: raftpb.MsgVoteResp, From: 1, To: 2, Term: 1},
- {Type: raftpb.MsgSnap, From: 1, To: 2, Term: 1, Snapshot: &raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 1000, Term: 1}, Data: data}},
- {Type: raftpb.MsgHeartbeat, From: 1, To: 2, Term: 1, Commit: 3},
- {Type: raftpb.MsgHeartbeatResp, From: 1, To: 2, Term: 1},
+ {Type: raftpb.MsgProp.Enum(), From: new(uint64(1)), To: new(uint64(2)), Entries: []*raftpb.Entry{{Data: data}}},
+ {Type: raftpb.MsgApp.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Index: new(uint64(3)), LogTerm: new(uint64(0)), Entries: []*raftpb.Entry{{Index: new(uint64(4)), Term: new(uint64(1)), Data: data}}, Commit: new(uint64(3))},
+ {Type: raftpb.MsgAppResp.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Index: new(uint64(3))},
+ {Type: raftpb.MsgVote.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Index: new(uint64(3)), LogTerm: new(uint64(0))},
+ {Type: raftpb.MsgVoteResp.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1))},
+ {Type: raftpb.MsgSnap.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Snapshot: &raftpb.Snapshot{Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(1000)), Term: new(uint64(1))}, Data: data}},
+ {Type: raftpb.MsgHeartbeat.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Commit: new(uint64(3))},
+ {Type: raftpb.MsgHeartbeatResp.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1))},
}
for i, tt := range tests {
- tr.Send([]raftpb.Message{tt})
+ tr.Send([]*raftpb.Message{tt})
msg := <-recvc
- if !reflect.DeepEqual(msg, tt) {
+ if !proto.Equal(msg, tt) {
t.Errorf("#%d: msg = %+v, want %+v", i, msg, tt)
}
}
@@ -101,7 +101,7 @@ func TestSendMessageWhenStreamIsBroken(t *testing.T) {
defer srv.Close()
// member 2
- recvc := make(chan raftpb.Message, 1)
+ recvc := make(chan *raftpb.Message, 1)
p := &fakeRaft{recvc: recvc}
tr2 := &Transport{
ID: types.ID(2),
@@ -131,7 +131,7 @@ func TestSendMessageWhenStreamIsBroken(t *testing.T) {
// TODO: remove this resend logic when we add retry logic into the code
case <-time.After(time.Millisecond):
n++
- tr.Send([]raftpb.Message{{Type: raftpb.MsgHeartbeat, From: 1, To: 2, Term: 1, Commit: 3}})
+ tr.Send([]*raftpb.Message{{Type: raftpb.MsgHeartbeat.Enum(), From: new(uint64(1)), To: new(uint64(2)), Term: new(uint64(1)), Commit: new(uint64(3))}})
case <-recvc:
if n > 50 {
t.Errorf("disconnection time = %dms, want < 50ms", n)
@@ -160,12 +160,12 @@ func waitStreamWorking(p *peer) bool {
}
type fakeRaft struct {
- recvc chan<- raftpb.Message
+ recvc chan<- *raftpb.Message
err error
removedID uint64
}
-func (p *fakeRaft) Process(ctx context.Context, m raftpb.Message) error {
+func (p *fakeRaft) Process(ctx context.Context, m *raftpb.Message) error {
select {
case p.recvc <- m:
default:
diff --git a/server/etcdserver/api/rafthttp/http.go b/server/etcdserver/api/rafthttp/http.go
index 2610240e5ace..93e996f89abf 100644
--- a/server/etcdserver/api/rafthttp/http.go
+++ b/server/etcdserver/api/rafthttp/http.go
@@ -26,6 +26,7 @@ import (
humanize "github.com/dustin/go-humanize"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -124,7 +125,7 @@ func (h *pipelineHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
var m raftpb.Message
- if err := m.Unmarshal(b); err != nil {
+ if err := proto.Unmarshal(b, &m); err != nil {
h.lg.Warn(
"failed to unmarshal Raft message",
zap.String("local-member-id", h.localID.String()),
@@ -135,9 +136,9 @@ func (h *pipelineHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- receivedBytes.WithLabelValues(types.ID(m.From).String()).Add(float64(len(b)))
+ receivedBytes.WithLabelValues(types.ID(m.GetFrom()).String()).Add(float64(len(b)))
- if err := h.r.Process(context.TODO(), m); err != nil {
+ if err := h.r.Process(context.TODO(), &m); err != nil {
var writerErr writerToResponse
switch {
case errors.As(err, &writerErr):
@@ -220,7 +221,7 @@ func (h *snapshotHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
dec := &messageDecoder{r: r.Body}
// let snapshots be very large since they can exceed 512MB for large installations
m, err := dec.decodeLimit(snapshotLimitByte)
- from := types.ID(m.From).String()
+ from := types.ID(m.GetFrom()).String()
if err != nil {
msg := fmt.Sprintf("failed to decode raft message (%v)", err)
h.lg.Warn(
@@ -235,15 +236,15 @@ func (h *snapshotHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- msgSize := m.Size()
+ msgSize := proto.Size(m)
receivedBytes.WithLabelValues(from).Add(float64(msgSize))
- if m.Type != raftpb.MsgSnap {
+ if m.GetType() != raftpb.MsgSnap {
h.lg.Warn(
"unexpected Raft message type",
zap.String("local-member-id", h.localID.String()),
zap.String("remote-snapshot-sender-id", from),
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
)
http.Error(w, "wrong raft message type", http.StatusBadRequest)
snapshotReceiveFailures.WithLabelValues(from).Inc()
@@ -259,21 +260,21 @@ func (h *snapshotHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"receiving database snapshot",
zap.String("local-member-id", h.localID.String()),
zap.String("remote-snapshot-sender-id", from),
- zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.Int("incoming-snapshot-message-size-bytes", msgSize),
zap.String("incoming-snapshot-message-size", humanize.Bytes(uint64(msgSize))),
)
// save incoming database snapshot.
- n, err := h.snapshotter.SaveDBFrom(r.Body, m.Snapshot.Metadata.Index)
+ n, err := h.snapshotter.SaveDBFrom(r.Body, m.Snapshot.Metadata.GetIndex())
if err != nil {
msg := fmt.Sprintf("failed to save KV snapshot (%v)", err)
h.lg.Warn(
"failed to save incoming database snapshot",
zap.String("local-member-id", h.localID.String()),
zap.String("remote-snapshot-sender-id", from),
- zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.Error(err),
)
http.Error(w, msg, http.StatusInternalServerError)
@@ -288,7 +289,7 @@ func (h *snapshotHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"received and saved database snapshot",
zap.String("local-member-id", h.localID.String()),
zap.String("remote-snapshot-sender-id", from),
- zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("incoming-snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.Int64("incoming-snapshot-size-bytes", n),
zap.String("incoming-snapshot-size", humanize.Bytes(uint64(n))),
zap.String("download-took", downloadTook.String()),
diff --git a/server/etcdserver/api/rafthttp/http_test.go b/server/etcdserver/api/rafthttp/http_test.go
index fb3e1f66eb17..5ba15860ecb2 100644
--- a/server/etcdserver/api/rafthttp/http_test.go
+++ b/server/etcdserver/api/rafthttp/http_test.go
@@ -48,7 +48,7 @@ func TestServeRaftPrefix(t *testing.T) {
// bad method
"GET",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{},
"0",
@@ -58,7 +58,7 @@ func TestServeRaftPrefix(t *testing.T) {
// bad method
"PUT",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{},
"0",
@@ -68,7 +68,7 @@ func TestServeRaftPrefix(t *testing.T) {
// bad method
"DELETE",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{},
"0",
@@ -94,7 +94,7 @@ func TestServeRaftPrefix(t *testing.T) {
// good request, wrong cluster ID
"POST",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{},
"1",
@@ -104,7 +104,7 @@ func TestServeRaftPrefix(t *testing.T) {
// good request, Processor failure
"POST",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{
err: &resWriterToError{code: http.StatusForbidden},
@@ -116,7 +116,7 @@ func TestServeRaftPrefix(t *testing.T) {
// good request, Processor failure
"POST",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{
err: &resWriterToError{code: http.StatusInternalServerError},
@@ -128,7 +128,7 @@ func TestServeRaftPrefix(t *testing.T) {
// good request, Processor failure
"POST",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{err: errors.New("blah")},
"0",
@@ -138,7 +138,7 @@ func TestServeRaftPrefix(t *testing.T) {
// good request
"POST",
bytes.NewReader(
- pbutil.MustMarshal(&raftpb.Message{}),
+ pbutil.MustMarshalMessage(&raftpb.Message{}),
),
&fakeRaft{},
"0",
@@ -357,8 +357,8 @@ type fakePeerGetter struct {
func (pg *fakePeerGetter) Get(id types.ID) Peer { return pg.peers[id] }
type fakePeer struct {
- msgs []raftpb.Message
- snapMsgs []snap.Message
+ msgs []*raftpb.Message
+ snapMsgs []*snap.Message
peerURLs types.URLs
connc chan *outgoingConn
paused bool
@@ -372,14 +372,14 @@ func newFakePeer() *fakePeer {
}
}
-func (pr *fakePeer) send(m raftpb.Message) {
+func (pr *fakePeer) send(m *raftpb.Message) {
if pr.paused {
return
}
pr.msgs = append(pr.msgs, m)
}
-func (pr *fakePeer) sendSnap(m snap.Message) {
+func (pr *fakePeer) sendSnap(m *snap.Message) {
if pr.paused {
return
}
diff --git a/server/etcdserver/api/rafthttp/msg_codec.go b/server/etcdserver/api/rafthttp/msg_codec.go
index 5444c01f8fdc..7e9c4d7fcac9 100644
--- a/server/etcdserver/api/rafthttp/msg_codec.go
+++ b/server/etcdserver/api/rafthttp/msg_codec.go
@@ -19,6 +19,8 @@ import (
"errors"
"io"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/pkg/v3/pbutil"
"go.etcd.io/raft/v3/raftpb"
)
@@ -30,10 +32,10 @@ type messageEncoder struct {
}
func (enc *messageEncoder) encode(m *raftpb.Message) error {
- if err := binary.Write(enc.w, binary.BigEndian, uint64(m.Size())); err != nil {
+ if err := binary.Write(enc.w, binary.BigEndian, uint64(proto.Size(m))); err != nil {
return err
}
- _, err := enc.w.Write(pbutil.MustMarshal(m))
+ _, err := enc.w.Write(pbutil.MustMarshalMessage(m))
return err
}
@@ -47,22 +49,22 @@ var (
ErrExceedSizeLimit = errors.New("rafthttp: error limit exceeded")
)
-func (dec *messageDecoder) decode() (raftpb.Message, error) {
+func (dec *messageDecoder) decode() (*raftpb.Message, error) {
return dec.decodeLimit(readBytesLimit)
}
-func (dec *messageDecoder) decodeLimit(numBytes uint64) (raftpb.Message, error) {
+func (dec *messageDecoder) decodeLimit(numBytes uint64) (*raftpb.Message, error) {
var m raftpb.Message
var l uint64
if err := binary.Read(dec.r, binary.BigEndian, &l); err != nil {
- return m, err
+ return nil, err
}
if l > numBytes {
- return m, ErrExceedSizeLimit
+ return nil, ErrExceedSizeLimit
}
buf := make([]byte, int(l))
if _, err := io.ReadFull(dec.r, buf); err != nil {
- return m, err
+ return nil, err
}
- return m, m.Unmarshal(buf)
+ return &m, proto.Unmarshal(buf, &m)
}
diff --git a/server/etcdserver/api/rafthttp/msg_codec_test.go b/server/etcdserver/api/rafthttp/msg_codec_test.go
index cde7916fc1b6..40b2cc6f117f 100644
--- a/server/etcdserver/api/rafthttp/msg_codec_test.go
+++ b/server/etcdserver/api/rafthttp/msg_codec_test.go
@@ -17,9 +17,10 @@ package rafthttp
import (
"bytes"
"errors"
- "reflect"
"testing"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/raft/v3/raftpb"
)
@@ -31,29 +32,29 @@ func TestMessage(t *testing.T) {
readBytesLimit = originalLimit
}()
tests := []struct {
- msg raftpb.Message
+ msg *raftpb.Message
encodeErr error
decodeErr error
}{
{
- raftpb.Message{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 1,
- LogTerm: 1,
- Index: 3,
- Entries: []raftpb.Entry{{Term: 1, Index: 4}},
+ &raftpb.Message{
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(1)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(3)),
+ Entries: []*raftpb.Entry{{Term: new(uint64(1)), Index: new(uint64(4))}},
},
nil,
nil,
},
{
- raftpb.Message{
- Type: raftpb.MsgProp,
- From: 1,
- To: 2,
- Entries: []raftpb.Entry{
+ &raftpb.Message{
+ Type: raftpb.MsgProp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Entries: []*raftpb.Entry{
{Data: []byte("some data")},
{Data: []byte("some data")},
{Data: []byte("some data")},
@@ -63,11 +64,11 @@ func TestMessage(t *testing.T) {
nil,
},
{
- raftpb.Message{
- Type: raftpb.MsgProp,
- From: 1,
- To: 2,
- Entries: []raftpb.Entry{
+ &raftpb.Message{
+ Type: raftpb.MsgProp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Entries: []*raftpb.Entry{
{Data: bytes.Repeat([]byte("a"), int(readBytesLimit+10))},
},
},
@@ -78,7 +79,7 @@ func TestMessage(t *testing.T) {
for i, tt := range tests {
b := &bytes.Buffer{}
enc := &messageEncoder{w: b}
- if err := enc.encode(&tt.msg); !errors.Is(err, tt.encodeErr) {
+ if err := enc.encode(tt.msg); !errors.Is(err, tt.encodeErr) {
t.Errorf("#%d: encode message error expected %v, got %v", i, tt.encodeErr, err)
continue
}
@@ -89,7 +90,7 @@ func TestMessage(t *testing.T) {
continue
}
if err == nil {
- if !reflect.DeepEqual(m, tt.msg) {
+ if !proto.Equal(m, tt.msg) {
t.Errorf("#%d: message = %+v, want %+v", i, m, tt.msg)
}
}
diff --git a/server/etcdserver/api/rafthttp/msgappv2_codec.go b/server/etcdserver/api/rafthttp/msgappv2_codec.go
index 59425aeea69d..cffa6aa469bb 100644
--- a/server/etcdserver/api/rafthttp/msgappv2_codec.go
+++ b/server/etcdserver/api/rafthttp/msgappv2_codec.go
@@ -20,6 +20,8 @@ import (
"io"
"time"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/pbutil"
stats "go.etcd.io/etcd/server/v3/etcdserver/api/v2stats"
@@ -90,7 +92,7 @@ func (enc *msgAppV2Encoder) encode(m *raftpb.Message) error {
if _, err := enc.w.Write(enc.uint8buf); err != nil {
return err
}
- case enc.index == m.Index && enc.term == m.LogTerm && m.LogTerm == m.Term:
+ case enc.index == m.GetIndex() && enc.term == m.GetLogTerm() && m.GetLogTerm() == m.GetTerm():
enc.uint8buf[0] = msgTypeAppEntries
if _, err := enc.w.Write(enc.uint8buf); err != nil {
return err
@@ -100,28 +102,31 @@ func (enc *msgAppV2Encoder) encode(m *raftpb.Message) error {
if _, err := enc.w.Write(enc.uint64buf); err != nil {
return err
}
+ opts := proto.MarshalOptions{}
for i := 0; i < len(m.Entries); i++ {
+ size := proto.Size(m.Entries[i])
// write length of entry
- binary.BigEndian.PutUint64(enc.uint64buf, uint64(m.Entries[i].Size()))
+ binary.BigEndian.PutUint64(enc.uint64buf, uint64(size))
if _, err := enc.w.Write(enc.uint64buf); err != nil {
return err
}
- if n := m.Entries[i].Size(); n < msgAppV2BufSize {
- if _, err := m.Entries[i].MarshalTo(enc.buf); err != nil {
+ if size < msgAppV2BufSize {
+ b, err := opts.MarshalAppend(enc.buf[:0], m.Entries[i])
+ if err != nil {
return err
}
- if _, err := enc.w.Write(enc.buf[:n]); err != nil {
+ if _, err := enc.w.Write(b); err != nil {
return err
}
} else {
- if _, err := enc.w.Write(pbutil.MustMarshal(&m.Entries[i])); err != nil {
+ if _, err := enc.w.Write(pbutil.MustMarshalMessage(m.Entries[i])); err != nil {
return err
}
}
enc.index++
}
// write commit index
- binary.BigEndian.PutUint64(enc.uint64buf, m.Commit)
+ binary.BigEndian.PutUint64(enc.uint64buf, m.GetCommit())
if _, err := enc.w.Write(enc.uint64buf); err != nil {
return err
}
@@ -131,18 +136,18 @@ func (enc *msgAppV2Encoder) encode(m *raftpb.Message) error {
return err
}
// write size of message
- if err := binary.Write(enc.w, binary.BigEndian, uint64(m.Size())); err != nil {
+ if err := binary.Write(enc.w, binary.BigEndian, uint64(proto.Size(m))); err != nil {
return err
}
// write message
- if _, err := enc.w.Write(pbutil.MustMarshal(m)); err != nil {
+ if _, err := enc.w.Write(pbutil.MustMarshalMessage(m)); err != nil {
return err
}
- enc.term = m.Term
- enc.index = m.Index
+ enc.term = m.GetTerm()
+ enc.index = m.GetIndex()
if l := len(m.Entries); l > 0 {
- enc.index = m.Entries[l-1].Index
+ enc.index = m.Entries[l-1].GetIndex()
}
enc.fs.Succ(time.Since(start))
}
@@ -171,78 +176,79 @@ func newMsgAppV2Decoder(r io.Reader, local, remote types.ID) *msgAppV2Decoder {
}
}
-func (dec *msgAppV2Decoder) decode() (raftpb.Message, error) {
+func (dec *msgAppV2Decoder) decode() (*raftpb.Message, error) {
var (
m raftpb.Message
typ uint8
)
if _, err := io.ReadFull(dec.r, dec.uint8buf); err != nil {
- return m, err
+ return nil, err
}
typ = dec.uint8buf[0]
switch typ {
case msgTypeLinkHeartbeat:
- return linkHeartbeatMessage, nil
+ return proto.Clone(&linkHeartbeatMessage).(*raftpb.Message), nil
case msgTypeAppEntries:
m = raftpb.Message{
- Type: raftpb.MsgApp,
- From: uint64(dec.remote),
- To: uint64(dec.local),
- Term: dec.term,
- LogTerm: dec.term,
- Index: dec.index,
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(dec.remote)),
+ To: new(uint64(dec.local)),
+ Term: new(dec.term),
+ LogTerm: new(dec.term),
+ Index: new(dec.index),
}
// decode entries
if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
- return m, err
+ return nil, err
}
l := binary.BigEndian.Uint64(dec.uint64buf)
- m.Entries = make([]raftpb.Entry, int(l))
+ m.Entries = make([]*raftpb.Entry, int(l))
for i := 0; i < int(l); i++ {
if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
- return m, err
+ return nil, err
}
size := binary.BigEndian.Uint64(dec.uint64buf)
var buf []byte
if size < msgAppV2BufSize {
buf = dec.buf[:size]
if _, err := io.ReadFull(dec.r, buf); err != nil {
- return m, err
+ return nil, err
}
} else {
buf = make([]byte, int(size))
if _, err := io.ReadFull(dec.r, buf); err != nil {
- return m, err
+ return nil, err
}
}
dec.index++
// 1 alloc
- pbutil.MustUnmarshal(&m.Entries[i], buf)
+ m.Entries[i] = &raftpb.Entry{}
+ pbutil.MustUnmarshalMessage(m.Entries[i], buf)
}
// decode commit index
if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
- return m, err
+ return nil, err
}
- m.Commit = binary.BigEndian.Uint64(dec.uint64buf)
+ m.Commit = new(binary.BigEndian.Uint64(dec.uint64buf))
case msgTypeApp:
var size uint64
if err := binary.Read(dec.r, binary.BigEndian, &size); err != nil {
- return m, err
+ return nil, err
}
buf := make([]byte, int(size))
if _, err := io.ReadFull(dec.r, buf); err != nil {
- return m, err
+ return nil, err
}
- pbutil.MustUnmarshal(&m, buf)
+ pbutil.MustUnmarshalMessage(&m, buf)
- dec.term = m.Term
- dec.index = m.Index
+ dec.term = m.GetTerm()
+ dec.index = m.GetIndex()
if l := len(m.Entries); l > 0 {
- dec.index = m.Entries[l-1].Index
+ dec.index = m.Entries[l-1].GetIndex()
}
default:
- return m, fmt.Errorf("failed to parse type %d in msgappv2 stream", typ)
+ return nil, fmt.Errorf("failed to parse type %d in msgappv2 stream", typ)
}
- return m, nil
+ return &m, nil
}
diff --git a/server/etcdserver/api/rafthttp/msgappv2_codec_test.go b/server/etcdserver/api/rafthttp/msgappv2_codec_test.go
index 50c7bd25c6d0..e397dc24ab6b 100644
--- a/server/etcdserver/api/rafthttp/msgappv2_codec_test.go
+++ b/server/etcdserver/api/rafthttp/msgappv2_codec_test.go
@@ -16,98 +16,101 @@ package rafthttp
import (
"bytes"
- "reflect"
"testing"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/client/pkg/v3/types"
stats "go.etcd.io/etcd/server/v3/etcdserver/api/v2stats"
"go.etcd.io/raft/v3/raftpb"
)
func TestMsgAppV2(t *testing.T) {
- tests := []raftpb.Message{
- linkHeartbeatMessage,
+ tests := []*raftpb.Message{
+ &linkHeartbeatMessage,
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 1,
- LogTerm: 1,
- Index: 0,
- Entries: []raftpb.Entry{
- {Term: 1, Index: 1, Data: []byte("some data")},
- {Term: 1, Index: 2, Data: []byte("some data")},
- {Term: 1, Index: 3, Data: []byte("some data")},
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(1)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(0)),
+ Entries: []*raftpb.Entry{
+ {Term: new(uint64(1)), Index: new(uint64(1)), Data: []byte("some data")},
+ {Term: new(uint64(1)), Index: new(uint64(2)), Data: []byte("some data")},
+ {Term: new(uint64(1)), Index: new(uint64(3)), Data: []byte("some data")},
},
},
// consecutive MsgApp
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 1,
- LogTerm: 1,
- Index: 3,
- Entries: []raftpb.Entry{
- {Term: 1, Index: 4, Data: []byte("some data")},
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(1)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(3)),
+ Entries: []*raftpb.Entry{
+ {Term: new(uint64(1)), Index: new(uint64(4)), Data: []byte("some data")},
},
+ Commit: new(uint64(0)),
},
- linkHeartbeatMessage,
+ &linkHeartbeatMessage,
// consecutive MsgApp after linkHeartbeatMessage
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 1,
- LogTerm: 1,
- Index: 4,
- Entries: []raftpb.Entry{
- {Term: 1, Index: 5, Data: []byte("some data")},
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(1)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(4)),
+ Entries: []*raftpb.Entry{
+ {Term: new(uint64(1)), Index: new(uint64(5)), Data: []byte("some data")},
},
+ Commit: new(uint64(0)),
},
// MsgApp with higher term
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 3,
- LogTerm: 1,
- Index: 5,
- Entries: []raftpb.Entry{
- {Term: 3, Index: 6, Data: []byte("some data")},
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(3)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(5)),
+ Entries: []*raftpb.Entry{
+ {Term: new(uint64(3)), Index: new(uint64(6)), Data: []byte("some data")},
},
},
- linkHeartbeatMessage,
+ &linkHeartbeatMessage,
// consecutive MsgApp
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 3,
- LogTerm: 2,
- Index: 6,
- Entries: []raftpb.Entry{
- {Term: 3, Index: 7, Data: []byte("some data")},
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(3)),
+ LogTerm: new(uint64(2)),
+ Index: new(uint64(6)),
+ Entries: []*raftpb.Entry{
+ {Term: new(uint64(3)), Index: new(uint64(7)), Data: []byte("some data")},
},
},
// consecutive empty MsgApp
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Term: 3,
- LogTerm: 2,
- Index: 7,
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Term: new(uint64(3)),
+ LogTerm: new(uint64(2)),
+ Index: new(uint64(7)),
Entries: nil,
},
- linkHeartbeatMessage,
+ &linkHeartbeatMessage,
}
b := &bytes.Buffer{}
enc := newMsgAppV2Encoder(b, &stats.FollowerStats{})
dec := newMsgAppV2Decoder(b, types.ID(2), types.ID(1))
for i, tt := range tests {
- if err := enc.encode(&tt); err != nil {
+ if err := enc.encode(tt); err != nil {
t.Errorf("#%d: unexpected encode message error: %v", i, err)
continue
}
@@ -116,7 +119,7 @@ func TestMsgAppV2(t *testing.T) {
t.Errorf("#%d: unexpected decode message error: %v", i, err)
continue
}
- if !reflect.DeepEqual(m, tt) {
+ if !proto.Equal(m, tt) {
t.Errorf("#%d: message = %+v, want %+v", i, m, tt)
}
}
diff --git a/server/etcdserver/api/rafthttp/peer.go b/server/etcdserver/api/rafthttp/peer.go
index c1e6ba127047..a6d6b4b4c1fe 100644
--- a/server/etcdserver/api/rafthttp/peer.go
+++ b/server/etcdserver/api/rafthttp/peer.go
@@ -65,11 +65,11 @@ type Peer interface {
// and has no promise that the message will be received by the remote.
// When it fails to send message out, it will report the status to underlying
// raft.
- send(m raftpb.Message)
+ send(m *raftpb.Message)
// sendSnap sends the merged snapshot message to the remote peer. Its behavior
// is similar to send.
- sendSnap(m snap.Message)
+ sendSnap(m *snap.Message)
// update updates the urls of remote peer.
update(urls types.URLs)
@@ -118,8 +118,8 @@ type peer struct {
msgAppV2Reader *streamReader
msgAppReader *streamReader
- recvc chan raftpb.Message
- propc chan raftpb.Message
+ recvc chan *raftpb.Message
+ propc chan *raftpb.Message
mu sync.Mutex
paused bool
@@ -164,8 +164,8 @@ func startPeer(t *Transport, urls types.URLs, peerID types.ID, fs *stats.Followe
writer: startStreamWriter(t.Logger, t.ID, peerID, status, fs, r),
pipeline: pipeline,
snapSender: newSnapshotSender(t, picker, peerID, status),
- recvc: make(chan raftpb.Message, recvBufSize),
- propc: make(chan raftpb.Message, maxPendingProposals),
+ recvc: make(chan *raftpb.Message, recvBufSize),
+ propc: make(chan *raftpb.Message, maxPendingProposals),
stopc: make(chan struct{}),
}
@@ -233,7 +233,7 @@ func startPeer(t *Transport, urls types.URLs, peerID types.ID, fs *stats.Followe
return p
}
-func (p *peer) send(m raftpb.Message) {
+func (p *peer) send(m *raftpb.Message) {
p.mu.Lock()
paused := p.paused
p.mu.Unlock()
@@ -246,26 +246,26 @@ func (p *peer) send(m raftpb.Message) {
select {
case writec <- m:
default:
- p.r.ReportUnreachable(m.To)
+ p.r.ReportUnreachable(m.GetTo())
if isMsgSnap(m) {
- p.r.ReportSnapshot(m.To, raft.SnapshotFailure)
+ p.r.ReportSnapshot(m.GetTo(), raft.SnapshotFailure)
}
if p.lg != nil {
p.lg.Warn(
"dropped internal Raft message since sending buffer is full",
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
zap.String("local-member-id", p.localID.String()),
- zap.String("from", types.ID(m.From).String()),
+ zap.String("from", types.ID(m.GetFrom()).String()),
zap.String("remote-peer-id", p.id.String()),
zap.String("remote-peer-name", name),
zap.Bool("remote-peer-active", p.status.isActive()),
)
}
- sentFailures.WithLabelValues(types.ID(m.To).String()).Inc()
+ sentFailures.WithLabelValues(types.ID(m.GetTo()).String()).Inc()
}
}
-func (p *peer) sendSnap(m snap.Message) {
+func (p *peer) sendSnap(m *snap.Message) {
go p.snapSender.send(m)
}
@@ -334,7 +334,7 @@ func (p *peer) stop() {
// pick picks a chan for sending the given message. The picked chan and the picked chan
// string name are returned.
-func (p *peer) pick(m raftpb.Message) (writec chan<- raftpb.Message, picked string) {
+func (p *peer) pick(m *raftpb.Message) (writec chan<- *raftpb.Message, picked string) {
var ok bool
// Considering MsgSnap may have a big size, e.g., 1G, and will block
// stream for a long time, only use one of the N pipelines to send MsgSnap.
@@ -348,6 +348,6 @@ func (p *peer) pick(m raftpb.Message) (writec chan<- raftpb.Message, picked stri
return p.pipeline.msgc, pipelineMsg
}
-func isMsgApp(m raftpb.Message) bool { return m.Type == raftpb.MsgApp }
+func isMsgApp(m *raftpb.Message) bool { return m.GetType() == raftpb.MsgApp }
-func isMsgSnap(m raftpb.Message) bool { return m.Type == raftpb.MsgSnap }
+func isMsgSnap(m *raftpb.Message) bool { return m.GetType() == raftpb.MsgSnap }
diff --git a/server/etcdserver/api/rafthttp/peer_test.go b/server/etcdserver/api/rafthttp/peer_test.go
index d1a4f679367a..0f82875e45c6 100644
--- a/server/etcdserver/api/rafthttp/peer_test.go
+++ b/server/etcdserver/api/rafthttp/peer_test.go
@@ -24,52 +24,52 @@ func TestPeerPick(t *testing.T) {
tests := []struct {
msgappWorking bool
messageWorking bool
- m raftpb.Message
+ m *raftpb.Message
wpicked string
}{
{
true, true,
- raftpb.Message{Type: raftpb.MsgSnap},
+ &raftpb.Message{Type: raftpb.MsgSnap.Enum()},
pipelineMsg,
},
{
true, true,
- raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ &raftpb.Message{Type: raftpb.MsgApp.Enum(), Term: new(uint64(1)), LogTerm: new(uint64(1))},
streamAppV2,
},
{
true, true,
- raftpb.Message{Type: raftpb.MsgProp},
+ &raftpb.Message{Type: raftpb.MsgProp.Enum()},
streamMsg,
},
{
true, true,
- raftpb.Message{Type: raftpb.MsgHeartbeat},
+ &raftpb.Message{Type: raftpb.MsgHeartbeat.Enum()},
streamMsg,
},
{
false, true,
- raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ &raftpb.Message{Type: raftpb.MsgApp.Enum(), Term: new(uint64(1)), LogTerm: new(uint64(1))},
streamMsg,
},
{
false, false,
- raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ &raftpb.Message{Type: raftpb.MsgApp.Enum(), Term: new(uint64(1)), LogTerm: new(uint64(1))},
pipelineMsg,
},
{
false, false,
- raftpb.Message{Type: raftpb.MsgProp},
+ &raftpb.Message{Type: raftpb.MsgProp.Enum()},
pipelineMsg,
},
{
false, false,
- raftpb.Message{Type: raftpb.MsgSnap},
+ &raftpb.Message{Type: raftpb.MsgSnap.Enum()},
pipelineMsg,
},
{
false, false,
- raftpb.Message{Type: raftpb.MsgHeartbeat},
+ &raftpb.Message{Type: raftpb.MsgHeartbeat.Enum()},
pipelineMsg,
},
}
diff --git a/server/etcdserver/api/rafthttp/pipeline.go b/server/etcdserver/api/rafthttp/pipeline.go
index 0790b58d03be..882bd1966c4c 100644
--- a/server/etcdserver/api/rafthttp/pipeline.go
+++ b/server/etcdserver/api/rafthttp/pipeline.go
@@ -24,6 +24,7 @@ import (
"time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/pbutil"
@@ -54,7 +55,7 @@ type pipeline struct {
// deprecate when we depercate v2 API
followerStats *stats.FollowerStats
- msgc chan raftpb.Message
+ msgc chan *raftpb.Message
// wait for the handling routines
wg sync.WaitGroup
stopc chan struct{}
@@ -62,7 +63,7 @@ type pipeline struct {
func (p *pipeline) start() {
p.stopc = make(chan struct{})
- p.msgc = make(chan raftpb.Message, pipelineBufSize)
+ p.msgc = make(chan *raftpb.Message, pipelineBufSize)
p.wg.Add(connPerPipeline)
for i := 0; i < connPerPipeline; i++ {
go p.handle()
@@ -97,7 +98,7 @@ func (p *pipeline) handle() {
select {
case m := <-p.msgc:
start := time.Now()
- err := p.post(pbutil.MustMarshal(&m))
+ err := p.post(pbutil.MustMarshalMessage(m))
end := time.Now()
if err != nil {
@@ -106,11 +107,11 @@ func (p *pipeline) handle() {
if isMsgApp(m) && p.followerStats != nil {
p.followerStats.Fail()
}
- p.raft.ReportUnreachable(m.To)
+ p.raft.ReportUnreachable(m.GetTo())
if isMsgSnap(m) {
- p.raft.ReportSnapshot(m.To, raft.SnapshotFailure)
+ p.raft.ReportSnapshot(m.GetTo(), raft.SnapshotFailure)
}
- sentFailures.WithLabelValues(types.ID(m.To).String()).Inc()
+ sentFailures.WithLabelValues(types.ID(m.GetTo()).String()).Inc()
continue
}
@@ -119,9 +120,9 @@ func (p *pipeline) handle() {
p.followerStats.Succ(end.Sub(start))
}
if isMsgSnap(m) {
- p.raft.ReportSnapshot(m.To, raft.SnapshotFinish)
+ p.raft.ReportSnapshot(m.GetTo(), raft.SnapshotFinish)
}
- sentBytes.WithLabelValues(types.ID(m.To).String()).Add(float64(m.Size()))
+ sentBytes.WithLabelValues(types.ID(m.GetTo()).String()).Add(float64(proto.Size(m)))
case <-p.stopc:
return
}
diff --git a/server/etcdserver/api/rafthttp/pipeline_test.go b/server/etcdserver/api/rafthttp/pipeline_test.go
index 316918ba5b6a..32c48b5a5be9 100644
--- a/server/etcdserver/api/rafthttp/pipeline_test.go
+++ b/server/etcdserver/api/rafthttp/pipeline_test.go
@@ -40,7 +40,7 @@ func TestPipelineSend(t *testing.T) {
tp := &Transport{pipelineRt: tr}
p := startTestPipeline(t, tp, picker)
- p.msgc <- raftpb.Message{Type: raftpb.MsgApp}
+ p.msgc <- &raftpb.Message{Type: raftpb.MsgApp.Enum()}
tr.rec.Wait(1)
p.stop()
if p.followerStats.Counts.Success != 1 {
@@ -58,7 +58,7 @@ func TestPipelineKeepSendingWhenPostError(t *testing.T) {
defer p.stop()
for i := 0; i < 50; i++ {
- p.msgc <- raftpb.Message{Type: raftpb.MsgApp}
+ p.msgc <- &raftpb.Message{Type: raftpb.MsgApp.Enum()}
}
_, err := tr.rec.Wait(50)
@@ -78,7 +78,7 @@ func TestPipelineExceedMaximumServing(t *testing.T) {
// nothing can go out as we block the sender
for i := 0; i < connPerPipeline+pipelineBufSize; i++ {
select {
- case p.msgc <- raftpb.Message{}:
+ case p.msgc <- &raftpb.Message{}:
case <-time.After(time.Second):
t.Errorf("failed to send out message")
}
@@ -86,7 +86,7 @@ func TestPipelineExceedMaximumServing(t *testing.T) {
// try to send a data when we are sure the buffer is full
select {
- case p.msgc <- raftpb.Message{}:
+ case p.msgc <- &raftpb.Message{}:
t.Errorf("unexpected message sendout")
default:
}
@@ -96,7 +96,7 @@ func TestPipelineExceedMaximumServing(t *testing.T) {
// It could send new data after previous ones succeed
select {
- case p.msgc <- raftpb.Message{}:
+ case p.msgc <- &raftpb.Message{}:
case <-time.After(time.Second):
t.Errorf("failed to send out message")
}
@@ -111,7 +111,7 @@ func TestPipelineSendFailed(t *testing.T) {
tp := &Transport{pipelineRt: rt}
p := startTestPipeline(t, tp, picker)
- p.msgc <- raftpb.Message{Type: raftpb.MsgApp}
+ p.msgc <- &raftpb.Message{Type: raftpb.MsgApp.Enum()}
if _, err := rt.rec.Wait(1); err != nil {
t.Fatal(err)
}
@@ -219,7 +219,7 @@ func TestStopBlockedPipeline(t *testing.T) {
p := startTestPipeline(t, tp, picker)
// send many messages that most of them will be blocked in buffer
for i := 0; i < connPerPipeline*10; i++ {
- p.msgc <- raftpb.Message{}
+ p.msgc <- &raftpb.Message{}
}
done := make(chan struct{})
diff --git a/server/etcdserver/api/rafthttp/remote.go b/server/etcdserver/api/rafthttp/remote.go
index 3eb2f386806f..801c6fcc6cb0 100644
--- a/server/etcdserver/api/rafthttp/remote.go
+++ b/server/etcdserver/api/rafthttp/remote.go
@@ -51,7 +51,7 @@ func startRemote(tr *Transport, urls types.URLs, id types.ID) *remote {
}
}
-func (g *remote) send(m raftpb.Message) {
+func (g *remote) send(m *raftpb.Message) {
select {
case g.pipeline.msgc <- m:
default:
@@ -59,9 +59,9 @@ func (g *remote) send(m raftpb.Message) {
if g.lg != nil {
g.lg.Warn(
"dropped internal Raft message since sending buffer is full (overloaded network)",
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
zap.String("local-member-id", g.localID.String()),
- zap.String("from", types.ID(m.From).String()),
+ zap.String("from", types.ID(m.GetFrom()).String()),
zap.String("remote-peer-id", g.id.String()),
zap.Bool("remote-peer-active", g.status.isActive()),
)
@@ -70,15 +70,15 @@ func (g *remote) send(m raftpb.Message) {
if g.lg != nil {
g.lg.Warn(
"dropped Raft message since sending buffer is full (overloaded network)",
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
zap.String("local-member-id", g.localID.String()),
- zap.String("from", types.ID(m.From).String()),
+ zap.String("from", types.ID(m.GetFrom()).String()),
zap.String("remote-peer-id", g.id.String()),
zap.Bool("remote-peer-active", g.status.isActive()),
)
}
}
- sentFailures.WithLabelValues(types.ID(m.To).String()).Inc()
+ sentFailures.WithLabelValues(types.ID(m.GetTo()).String()).Inc()
}
}
diff --git a/server/etcdserver/api/rafthttp/snapshot_sender.go b/server/etcdserver/api/rafthttp/snapshot_sender.go
index 8dbc11724b33..4deec2c88311 100644
--- a/server/etcdserver/api/rafthttp/snapshot_sender.go
+++ b/server/etcdserver/api/rafthttp/snapshot_sender.go
@@ -64,11 +64,11 @@ func newSnapshotSender(tr *Transport, picker *urlPicker, to types.ID, status *pe
func (s *snapshotSender) stop() { close(s.stopc) }
-func (s *snapshotSender) send(merged snap.Message) {
+func (s *snapshotSender) send(merged *snap.Message) {
start := time.Now()
m := merged.Message
- to := types.ID(m.To).String()
+ to := types.ID(m.GetTo()).String()
body := createSnapBody(s.tr.Logger, merged)
defer body.Close()
@@ -81,7 +81,7 @@ func (s *snapshotSender) send(merged snap.Message) {
if s.tr.Logger != nil {
s.tr.Logger.Info(
"sending database snapshot",
- zap.Uint64("snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.String("remote-peer-id", to),
zap.Uint64("bytes", snapshotSizeVal),
zap.String("size", snapshotSize),
@@ -99,7 +99,7 @@ func (s *snapshotSender) send(merged snap.Message) {
if s.tr.Logger != nil {
s.tr.Logger.Warn(
"failed to send database snapshot",
- zap.Uint64("snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.String("remote-peer-id", to),
zap.Uint64("bytes", snapshotSizeVal),
zap.String("size", snapshotSize),
@@ -115,22 +115,22 @@ func (s *snapshotSender) send(merged snap.Message) {
s.picker.unreachable(u)
s.status.deactivate(failureType{source: sendSnap, action: "post"}, err.Error())
- s.r.ReportUnreachable(m.To)
+ s.r.ReportUnreachable(m.GetTo())
// report SnapshotFailure to raft state machine. After raft state
// machine knows about it, it would pause a while and retry sending
// new snapshot message.
- s.r.ReportSnapshot(m.To, raft.SnapshotFailure)
+ s.r.ReportSnapshot(m.GetTo(), raft.SnapshotFailure)
sentFailures.WithLabelValues(to).Inc()
snapshotSendFailures.WithLabelValues(to).Inc()
return
}
s.status.activate()
- s.r.ReportSnapshot(m.To, raft.SnapshotFinish)
+ s.r.ReportSnapshot(m.GetTo(), raft.SnapshotFinish)
if s.tr.Logger != nil {
s.tr.Logger.Info(
"sent database snapshot",
- zap.Uint64("snapshot-index", m.Snapshot.Metadata.Index),
+ zap.Uint64("snapshot-index", m.Snapshot.Metadata.GetIndex()),
zap.String("remote-peer-id", to),
zap.Uint64("bytes", snapshotSizeVal),
zap.String("size", snapshotSize),
@@ -182,11 +182,11 @@ func (s *snapshotSender) post(req *http.Request) (err error) {
}
}
-func createSnapBody(lg *zap.Logger, merged snap.Message) io.ReadCloser {
+func createSnapBody(lg *zap.Logger, merged *snap.Message) io.ReadCloser {
buf := new(bytes.Buffer)
enc := &messageEncoder{w: buf}
// encode raft message
- if err := enc.encode(&merged.Message); err != nil {
+ if err := enc.encode(merged.Message); err != nil {
if lg != nil {
lg.Panic("failed to encode message", zap.Error(err))
}
diff --git a/server/etcdserver/api/rafthttp/snapshot_test.go b/server/etcdserver/api/rafthttp/snapshot_test.go
index 8f319fe52aa0..8e9af15c50a2 100644
--- a/server/etcdserver/api/rafthttp/snapshot_test.go
+++ b/server/etcdserver/api/rafthttp/snapshot_test.go
@@ -37,7 +37,7 @@ func (s strReaderCloser) Close() error { return nil }
func TestSnapshotSend(t *testing.T) {
tests := []struct {
- m raftpb.Message
+ m *raftpb.Message
rc io.ReadCloser
size int64
@@ -46,7 +46,7 @@ func TestSnapshotSend(t *testing.T) {
}{
// sent and receive with no errors
{
- m: raftpb.Message{Type: raftpb.MsgSnap, To: 1, Snapshot: &raftpb.Snapshot{}},
+ m: &raftpb.Message{Type: raftpb.MsgSnap.Enum(), To: new(uint64(1)), Snapshot: &raftpb.Snapshot{}},
rc: strReaderCloser{strings.NewReader("hello")},
size: 5,
@@ -55,7 +55,7 @@ func TestSnapshotSend(t *testing.T) {
},
// error when reading snapshot for send
{
- m: raftpb.Message{Type: raftpb.MsgSnap, To: 1, Snapshot: &raftpb.Snapshot{}},
+ m: &raftpb.Message{Type: raftpb.MsgSnap.Enum(), To: new(uint64(1)), Snapshot: &raftpb.Snapshot{}},
rc: &errReadCloser{fmt.Errorf("snapshot error")},
size: 1,
@@ -64,7 +64,7 @@ func TestSnapshotSend(t *testing.T) {
},
// sends less than the given snapshot length
{
- m: raftpb.Message{Type: raftpb.MsgSnap, To: 1, Snapshot: &raftpb.Snapshot{}},
+ m: &raftpb.Message{Type: raftpb.MsgSnap.Enum(), To: new(uint64(1)), Snapshot: &raftpb.Snapshot{}},
rc: strReaderCloser{strings.NewReader("hello")},
size: 10000,
@@ -73,7 +73,7 @@ func TestSnapshotSend(t *testing.T) {
},
// sends less than actual snapshot length
{
- m: raftpb.Message{Type: raftpb.MsgSnap, To: 1, Snapshot: &raftpb.Snapshot{}},
+ m: &raftpb.Message{Type: raftpb.MsgSnap.Enum(), To: new(uint64(1)), Snapshot: &raftpb.Snapshot{}},
rc: strReaderCloser{strings.NewReader("hello")},
size: 1,
@@ -107,7 +107,7 @@ func testSnapshotSend(t *testing.T, sm *snap.Message) (bool, []os.DirEntry) {
snapsend := newSnapshotSender(tr, picker, types.ID(1), newPeerStatus(zaptest.NewLogger(t), types.ID(0), types.ID(1)))
defer snapsend.stop()
- snapsend.send(*sm)
+ snapsend.send(sm)
sent := false
select {
diff --git a/server/etcdserver/api/rafthttp/stream.go b/server/etcdserver/api/rafthttp/stream.go
index fa02f42b9b9c..3619c3933619 100644
--- a/server/etcdserver/api/rafthttp/stream.go
+++ b/server/etcdserver/api/rafthttp/stream.go
@@ -28,6 +28,7 @@ import (
"github.com/coreos/go-semver/semver"
"go.uber.org/zap"
"golang.org/x/time/rate"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/transport"
@@ -60,6 +61,7 @@ var (
"3.4.0": {streamTypeMsgAppV2, streamTypeMessage},
"3.5.0": {streamTypeMsgAppV2, streamTypeMessage},
"3.6.0": {streamTypeMsgAppV2, streamTypeMessage},
+ "3.7.0": {streamTypeMsgAppV2, streamTypeMessage},
}
)
@@ -93,10 +95,13 @@ func (t streamType) String() string {
// linkHeartbeatMessage is a special message used as heartbeat message in
// link layer. It never conflicts with messages from raft because raft
// doesn't send out messages without From and To fields.
-var linkHeartbeatMessage = raftpb.Message{Type: raftpb.MsgHeartbeat}
+var (
+ linkHeartbeatMessage = raftpb.Message{Type: raftpb.MsgHeartbeat.Enum()}
+ linkHeartbeatSize = proto.Size(&linkHeartbeatMessage)
+)
func isLinkHeartbeatMessage(m *raftpb.Message) bool {
- return m.Type == raftpb.MsgHeartbeat && m.From == 0 && m.To == 0
+ return m.GetType() == raftpb.MsgHeartbeat && m.GetFrom() == 0 && m.GetTo() == 0
}
type outgoingConn struct {
@@ -124,7 +129,7 @@ type streamWriter struct {
closer io.Closer
working bool
- msgc chan raftpb.Message
+ msgc chan *raftpb.Message
connc chan *outgoingConn
stopc chan struct{}
done chan struct{}
@@ -142,7 +147,7 @@ func startStreamWriter(lg *zap.Logger, local, id types.ID, status *peerStatus, f
status: status,
fs: fs,
r: r,
- msgc: make(chan raftpb.Message, streamBufSize),
+ msgc: make(chan *raftpb.Message, streamBufSize),
connc: make(chan *outgoingConn),
stopc: make(chan struct{}),
done: make(chan struct{}),
@@ -153,7 +158,7 @@ func startStreamWriter(lg *zap.Logger, local, id types.ID, status *peerStatus, f
func (cw *streamWriter) run() {
var (
- msgc chan raftpb.Message
+ msgc chan *raftpb.Message
heartbeatc <-chan time.Time
t streamType
enc encoder
@@ -175,8 +180,8 @@ func (cw *streamWriter) run() {
for {
select {
case <-heartbeatc:
- err := enc.encode(&linkHeartbeatMessage)
- unflushed += linkHeartbeatMessage.Size()
+ err := enc.encode(proto.Clone(&linkHeartbeatMessage).(*raftpb.Message))
+ unflushed += linkHeartbeatSize
if err == nil {
flusher.Flush()
batched = 0
@@ -200,9 +205,9 @@ func (cw *streamWriter) run() {
heartbeatc, msgc = nil, nil
case m := <-msgc:
- err := enc.encode(&m)
+ err := enc.encode(m)
if err == nil {
- unflushed += m.Size()
+ unflushed += proto.Size(m)
if len(msgc) == 0 || batched > streamBufSize/2 {
flusher.Flush()
@@ -227,7 +232,7 @@ func (cw *streamWriter) run() {
)
}
heartbeatc, msgc = nil, nil
- cw.r.ReportUnreachable(m.To)
+ cw.r.ReportUnreachable(m.GetTo())
sentFailures.WithLabelValues(cw.peerID.String()).Inc()
case conn := <-cw.connc:
@@ -302,7 +307,7 @@ func (cw *streamWriter) run() {
}
}
-func (cw *streamWriter) writec() (chan<- raftpb.Message, bool) {
+func (cw *streamWriter) writec() (chan<- *raftpb.Message, bool) {
cw.mu.Lock()
defer cw.mu.Unlock()
return cw.msgc, cw.working
@@ -330,7 +335,7 @@ func (cw *streamWriter) closeUnlocked() bool {
if len(cw.msgc) > 0 {
cw.r.ReportUnreachable(uint64(cw.peerID))
}
- cw.msgc = make(chan raftpb.Message, streamBufSize)
+ cw.msgc = make(chan *raftpb.Message, streamBufSize)
cw.working = false
return true
}
@@ -360,8 +365,8 @@ type streamReader struct {
tr *Transport
picker *urlPicker
status *peerStatus
- recvc chan<- raftpb.Message
- propc chan<- raftpb.Message
+ recvc chan<- *raftpb.Message
+ propc chan<- *raftpb.Message
rl *rate.Limiter // alters the frequency of dial retrial attempts
@@ -497,9 +502,9 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
return err
}
- // gofail-go: var raftDropHeartbeat struct{}
+ // gofail: var raftDropHeartbeat struct{}
// continue labelRaftDropHeartbeat
- receivedBytes.WithLabelValues(types.ID(m.From).String()).Add(float64(m.Size()))
+ receivedBytes.WithLabelValues(types.ID(m.GetFrom()).String()).Add(float64(proto.Size(m)))
cr.mu.Lock()
paused := cr.paused
@@ -509,7 +514,7 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
continue
}
- if isLinkHeartbeatMessage(&m) {
+ if isLinkHeartbeatMessage(m) {
// raft is not interested in link layer
// heartbeat message, so we should ignore
// it.
@@ -517,7 +522,7 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
}
recvc := cr.recvc
- if m.Type == raftpb.MsgProp {
+ if m.GetType() == raftpb.MsgProp {
recvc = cr.propc
}
@@ -528,10 +533,10 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
if cr.lg != nil {
cr.lg.Warn(
"dropped internal Raft message since receiving buffer is full (overloaded network)",
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
zap.String("local-member-id", cr.tr.ID.String()),
- zap.String("from", types.ID(m.From).String()),
- zap.String("remote-peer-id", types.ID(m.To).String()),
+ zap.String("from", types.ID(m.GetFrom()).String()),
+ zap.String("remote-peer-id", types.ID(m.GetTo()).String()),
zap.Bool("remote-peer-active", cr.status.isActive()),
)
}
@@ -539,15 +544,15 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
if cr.lg != nil {
cr.lg.Warn(
"dropped Raft message since receiving buffer is full (overloaded network)",
- zap.String("message-type", m.Type.String()),
+ zap.String("message-type", m.GetType().String()),
zap.String("local-member-id", cr.tr.ID.String()),
- zap.String("from", types.ID(m.From).String()),
- zap.String("remote-peer-id", types.ID(m.To).String()),
+ zap.String("from", types.ID(m.GetFrom()).String()),
+ zap.String("remote-peer-id", types.ID(m.GetTo()).String()),
zap.Bool("remote-peer-active", cr.status.isActive()),
)
}
}
- recvFailures.WithLabelValues(types.ID(m.From).String()).Inc()
+ recvFailures.WithLabelValues(types.ID(m.GetFrom()).String()).Inc()
}
}
}
@@ -629,6 +634,7 @@ func (cr *streamReader) dial(t streamType) (io.ReadCloser, error) {
case http.StatusPreconditionFailed:
b, err := io.ReadAll(resp.Body)
if err != nil {
+ resp.Body.Close()
cr.picker.unreachable(u)
return nil, err
}
diff --git a/server/etcdserver/api/rafthttp/stream_test.go b/server/etcdserver/api/rafthttp/stream_test.go
index a50961b96f06..f3adf991db0a 100644
--- a/server/etcdserver/api/rafthttp/stream_test.go
+++ b/server/etcdserver/api/rafthttp/stream_test.go
@@ -15,12 +15,10 @@
package rafthttp
import (
- "context"
"errors"
"io"
"net/http"
"net/http/httptest"
- "reflect"
"sync"
"testing"
"time"
@@ -28,6 +26,7 @@ import (
"github.com/coreos/go-semver/semver"
"go.uber.org/zap/zaptest"
"golang.org/x/time/rate"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -66,7 +65,7 @@ func TestStreamWriterAttachOutgoingConn(t *testing.T) {
// if prevwfc == nil, the first connection may be pending, but the first
// msgc is already available since it's set on calling startStreamwriter
msgc, _ := sw.writec()
- msgc <- raftpb.Message{}
+ msgc <- &raftpb.Message{}
select {
case <-wfc.writec:
@@ -97,7 +96,7 @@ func TestStreamWriterAttachBadOutgoingConn(t *testing.T) {
wfc := newFakeWriteFlushCloser(errors.New("blah"))
sw.attach(&outgoingConn{t: streamTypeMessage, Writer: wfc, Flusher: wfc, Closer: wfc})
- sw.msgc <- raftpb.Message{}
+ sw.msgc <- &raftpb.Message{}
select {
case <-wfc.closed:
case <-time.After(time.Second):
@@ -116,7 +115,7 @@ func TestStreamReaderDialRequest(t *testing.T) {
peerID: types.ID(2),
tr: &Transport{streamRt: tr, ClusterID: types.ID(1), ID: types.ID(1)},
picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
- ctx: context.Background(),
+ ctx: t.Context(),
}
sr.dial(tt)
@@ -171,7 +170,7 @@ func TestStreamReaderDialResult(t *testing.T) {
tr: &Transport{streamRt: tr, ClusterID: types.ID(1)},
picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
errorc: make(chan error, 1),
- ctx: context.Background(),
+ ctx: t.Context(),
}
_, err := sr.dial(streamTypeMessage)
@@ -253,7 +252,7 @@ func TestStreamReaderDialDetectUnsupport(t *testing.T) {
peerID: types.ID(2),
tr: &Transport{streamRt: tr, ClusterID: types.ID(1)},
picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
- ctx: context.Background(),
+ ctx: t.Context(),
}
_, err := sr.dial(typ)
@@ -266,26 +265,26 @@ func TestStreamReaderDialDetectUnsupport(t *testing.T) {
// TestStream tests that streamReader and streamWriter can build stream to
// send messages between each other.
func TestStream(t *testing.T) {
- recvc := make(chan raftpb.Message, streamBufSize)
- propc := make(chan raftpb.Message, streamBufSize)
- msgapp := raftpb.Message{
- Type: raftpb.MsgApp,
- From: 2,
- To: 1,
- Term: 1,
- LogTerm: 1,
- Index: 3,
- Entries: []raftpb.Entry{{Term: 1, Index: 4}},
+ recvc := make(chan *raftpb.Message, streamBufSize)
+ propc := make(chan *raftpb.Message, streamBufSize)
+ msgapp := &raftpb.Message{
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(2)),
+ To: new(uint64(1)),
+ Term: new(uint64(1)),
+ LogTerm: new(uint64(1)),
+ Index: new(uint64(3)),
+ Entries: []*raftpb.Entry{{Term: new(uint64(1)), Index: new(uint64(4))}},
}
tests := []struct {
t streamType
- m raftpb.Message
- wc chan raftpb.Message
+ m *raftpb.Message
+ wc chan *raftpb.Message
}{
{
streamTypeMessage,
- raftpb.Message{Type: raftpb.MsgProp, To: 2},
+ &raftpb.Message{Type: raftpb.MsgProp.Enum(), To: new(uint64(2))},
propc,
},
{
@@ -324,7 +323,7 @@ func TestStream(t *testing.T) {
sr.start()
// wait for stream to work
- var writec chan<- raftpb.Message
+ var writec chan<- *raftpb.Message
for {
var ok bool
if writec, ok = sw.writec(); ok {
@@ -334,13 +333,13 @@ func TestStream(t *testing.T) {
}
writec <- tt.m
- var m raftpb.Message
+ var m *raftpb.Message
select {
case m = <-tt.wc:
case <-time.After(time.Second):
t.Fatalf("#%d: failed to receive message from the channel", i)
}
- if !reflect.DeepEqual(m, tt.m) {
+ if !proto.Equal(m, tt.m) {
t.Fatalf("#%d: message = %+v, want %+v", i, m, tt.m)
}
diff --git a/server/etcdserver/api/rafthttp/transport.go b/server/etcdserver/api/rafthttp/transport.go
index b376d578b6c4..3b93b89c85d2 100644
--- a/server/etcdserver/api/rafthttp/transport.go
+++ b/server/etcdserver/api/rafthttp/transport.go
@@ -23,6 +23,7 @@ import (
"github.com/xiang90/probing"
"go.uber.org/zap"
"golang.org/x/time/rate"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/transport"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -33,7 +34,7 @@ import (
)
type Raft interface {
- Process(ctx context.Context, m raftpb.Message) error
+ Process(ctx context.Context, m *raftpb.Message) error
IsIDRemoved(id uint64) bool
ReportUnreachable(id uint64)
ReportSnapshot(id uint64, status raft.SnapshotStatus)
@@ -54,10 +55,10 @@ type Transporter interface {
// to an existing peer in the transport.
// If the id cannot be found in the transport, the message
// will be ignored.
- Send(m []raftpb.Message)
+ Send(m []*raftpb.Message)
// SendSnapshot sends out the given snapshot message to a remote peer.
// The behavior of SendSnapshot is similar to Send.
- SendSnapshot(m snap.Message)
+ SendSnapshot(m *snap.Message)
// AddRemote adds a remote with given peer urls into the transport.
// A remote helps newly joined member to catch up the progress of cluster,
// and will not be used after that.
@@ -172,13 +173,13 @@ func (t *Transport) Get(id types.ID) Peer {
return t.peers[id]
}
-func (t *Transport) Send(msgs []raftpb.Message) {
+func (t *Transport) Send(msgs []*raftpb.Message) {
for _, m := range msgs {
- if m.To == 0 {
+ if m.GetTo() == 0 {
// ignore intentionally dropped message
continue
}
- to := types.ID(m.To)
+ to := types.ID(m.GetTo())
t.mu.RLock()
p, pok := t.peers[to]
@@ -187,7 +188,7 @@ func (t *Transport) Send(msgs []raftpb.Message) {
if pok {
if isMsgApp(m) {
- t.ServerStats.SendAppendReq(m.Size())
+ t.ServerStats.SendAppendReq(proto.Size(m))
}
p.send(m)
continue
@@ -201,7 +202,7 @@ func (t *Transport) Send(msgs []raftpb.Message) {
if t.Logger != nil {
t.Logger.Debug(
"ignored message send request; unknown remote peer target",
- zap.String("type", m.Type.String()),
+ zap.String("type", m.GetType().String()),
zap.String("unknown-target-peer-id", to.String()),
)
}
@@ -405,10 +406,10 @@ func (t *Transport) ActiveSince(id types.ID) time.Time {
return time.Time{}
}
-func (t *Transport) SendSnapshot(m snap.Message) {
+func (t *Transport) SendSnapshot(m *snap.Message) {
t.mu.Lock()
defer t.mu.Unlock()
- p := t.peers[types.ID(m.To)]
+ p := t.peers[types.ID(m.GetTo())]
if p == nil {
m.CloseWithError(errMemberNotFound)
return
diff --git a/server/etcdserver/api/rafthttp/transport_bench_test.go b/server/etcdserver/api/rafthttp/transport_bench_test.go
index 646fa8ee2201..61fb4a0816b4 100644
--- a/server/etcdserver/api/rafthttp/transport_bench_test.go
+++ b/server/etcdserver/api/rafthttp/transport_bench_test.go
@@ -69,15 +69,17 @@ func BenchmarkSendingMsgApp(b *testing.B) {
b.ResetTimer()
data := make([]byte, 64)
for i := 0; i < b.N; i++ {
- tr.Send([]raftpb.Message{
+ idx := uint64(i)
+ idxPlusOne := uint64(i + 1)
+ tr.Send([]*raftpb.Message{
{
- Type: raftpb.MsgApp,
- From: 1,
- To: 2,
- Index: uint64(i),
- Entries: []raftpb.Entry{
+ Type: raftpb.MsgApp.Enum(),
+ From: new(uint64(1)),
+ To: new(uint64(2)),
+ Index: &idx,
+ Entries: []*raftpb.Entry{
{
- Index: uint64(i + 1),
+ Index: &idxPlusOne,
Data: data,
},
},
@@ -96,7 +98,7 @@ type countRaft struct {
cnt int
}
-func (r *countRaft) Process(ctx context.Context, m raftpb.Message) error {
+func (r *countRaft) Process(ctx context.Context, m *raftpb.Message) error {
r.mu.Lock()
defer r.mu.Unlock()
r.cnt++
diff --git a/server/etcdserver/api/rafthttp/transport_test.go b/server/etcdserver/api/rafthttp/transport_test.go
index 92adb01c4908..513a48ec8478 100644
--- a/server/etcdserver/api/rafthttp/transport_test.go
+++ b/server/etcdserver/api/rafthttp/transport_test.go
@@ -20,8 +20,10 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/xiang90/probing"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/client/pkg/v3/testutil"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -38,31 +40,31 @@ func TestTransportSend(t *testing.T) {
ServerStats: stats.NewServerStats("", ""),
peers: map[types.ID]Peer{types.ID(1): peer1, types.ID(2): peer2},
}
- wmsgsIgnored := []raftpb.Message{
+ wmsgsIgnored := []*raftpb.Message{
// bad local message
- {Type: raftpb.MsgBeat},
+ {Type: raftpb.MsgBeat.Enum()},
// bad remote message
- {Type: raftpb.MsgProp, To: 3},
+ {Type: raftpb.MsgProp.Enum(), To: new(uint64(3))},
}
- wmsgsTo1 := []raftpb.Message{
+ wmsgsTo1 := []*raftpb.Message{
// good message
- {Type: raftpb.MsgProp, To: 1},
- {Type: raftpb.MsgApp, To: 1},
+ {Type: raftpb.MsgProp.Enum(), To: new(uint64(1))},
+ {Type: raftpb.MsgApp.Enum(), To: new(uint64(1))},
}
- wmsgsTo2 := []raftpb.Message{
+ wmsgsTo2 := []*raftpb.Message{
// good message
- {Type: raftpb.MsgProp, To: 2},
- {Type: raftpb.MsgApp, To: 2},
+ {Type: raftpb.MsgProp.Enum(), To: new(uint64(2))},
+ {Type: raftpb.MsgApp.Enum(), To: new(uint64(2))},
}
tr.Send(wmsgsIgnored)
tr.Send(wmsgsTo1)
tr.Send(wmsgsTo2)
- if !reflect.DeepEqual(peer1.msgs, wmsgsTo1) {
- t.Errorf("msgs to peer 1 = %+v, want %+v", peer1.msgs, wmsgsTo1)
+ if diff := cmp.Diff(wmsgsTo1, peer1.msgs, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected peer1 msgs (-want +got):\n%s", diff)
}
- if !reflect.DeepEqual(peer2.msgs, wmsgsTo2) {
- t.Errorf("msgs to peer 2 = %+v, want %+v", peer2.msgs, wmsgsTo2)
+ if diff := cmp.Diff(wmsgsTo2, peer2.msgs, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected peer2 msgs (-want +got):\n%s", diff)
}
}
@@ -76,10 +78,10 @@ func TestTransportCutMend(t *testing.T) {
tr.CutPeer(types.ID(1))
- wmsgsTo := []raftpb.Message{
+ wmsgsTo := []*raftpb.Message{
// good message
- {Type: raftpb.MsgProp, To: 1},
- {Type: raftpb.MsgApp, To: 1},
+ {Type: raftpb.MsgProp.Enum(), To: new(uint64(1))},
+ {Type: raftpb.MsgApp.Enum(), To: new(uint64(1))},
}
tr.Send(wmsgsTo)
@@ -90,8 +92,8 @@ func TestTransportCutMend(t *testing.T) {
tr.MendPeer(types.ID(1))
tr.Send(wmsgsTo)
- if !reflect.DeepEqual(peer1.msgs, wmsgsTo) {
- t.Errorf("msgs to peer 1 = %+v, want %+v", peer1.msgs, wmsgsTo)
+ if diff := cmp.Diff(wmsgsTo, peer1.msgs, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected peer1 msgs after mend (-want +got):\n%s", diff)
}
}
@@ -196,7 +198,7 @@ func TestTransportErrorc(t *testing.T) {
t.Fatalf("received unexpected from errorc")
case <-time.After(10 * time.Millisecond):
}
- tr.peers[1].send(raftpb.Message{})
+ tr.peers[1].send(&raftpb.Message{})
select {
case <-errorc:
diff --git a/server/etcdserver/api/rafthttp/util_test.go b/server/etcdserver/api/rafthttp/util_test.go
index de94813f2304..5e2cd48329ac 100644
--- a/server/etcdserver/api/rafthttp/util_test.go
+++ b/server/etcdserver/api/rafthttp/util_test.go
@@ -19,24 +19,24 @@ import (
"encoding/binary"
"io"
"net/http"
- "reflect"
"testing"
"github.com/coreos/go-semver/semver"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/raft/v3/raftpb"
)
func TestEntry(t *testing.T) {
- tests := []raftpb.Entry{
+ tests := []*raftpb.Entry{
{},
- {Term: 1, Index: 1},
- {Term: 1, Index: 1, Data: []byte("some data")},
+ {Term: new(uint64(1)), Index: new(uint64(1))},
+ {Term: new(uint64(1)), Index: new(uint64(1)), Data: []byte("some data")},
}
for i, tt := range tests {
b := &bytes.Buffer{}
- if err := writeEntryTo(b, &tt); err != nil {
+ if err := writeEntryTo(b, tt); err != nil {
t.Errorf("#%d: unexpected write ents error: %v", i, err)
continue
}
@@ -45,8 +45,8 @@ func TestEntry(t *testing.T) {
t.Errorf("#%d: unexpected read ents error: %v", i, err)
continue
}
- if !reflect.DeepEqual(ent, tt) {
- t.Errorf("#%d: ent = %+v, want %+v", i, ent, tt)
+ if !proto.Equal(&ent, tt) {
+ t.Errorf("#%d: ent = %+v, want %+v", i, &ent, tt)
}
}
}
@@ -196,11 +196,11 @@ func TestCheckVersionCompatibility(t *testing.T) {
}
func writeEntryTo(w io.Writer, ent *raftpb.Entry) error {
- size := ent.Size()
+ size := proto.Size(ent)
if err := binary.Write(w, binary.BigEndian, uint64(size)); err != nil {
return err
}
- b, err := ent.Marshal()
+ b, err := proto.Marshal(ent)
if err != nil {
return err
}
@@ -217,5 +217,5 @@ func readEntryFrom(r io.Reader, ent *raftpb.Entry) error {
if _, err := io.ReadFull(r, buf); err != nil {
return err
}
- return ent.Unmarshal(buf)
+ return proto.Unmarshal(buf, ent)
}
diff --git a/server/etcdserver/api/snap/message.go b/server/etcdserver/api/snap/message.go
index 2b4090c981d1..65a1b67115cc 100644
--- a/server/etcdserver/api/snap/message.go
+++ b/server/etcdserver/api/snap/message.go
@@ -17,6 +17,8 @@ package snap
import (
"io"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/pkg/v3/ioutil"
"go.etcd.io/raft/v3/raftpb"
)
@@ -30,17 +32,17 @@ import (
//
// User of Message should close the Message after sending it.
type Message struct {
- raftpb.Message
+ *raftpb.Message
ReadCloser io.ReadCloser
TotalSize int64
closeC chan bool
}
-func NewMessage(rs raftpb.Message, rc io.ReadCloser, rcSize int64) *Message {
+func NewMessage(rs *raftpb.Message, rc io.ReadCloser, rcSize int64) *Message {
return &Message{
Message: rs,
ReadCloser: ioutil.NewExactReadCloser(rc, rcSize),
- TotalSize: int64(rs.Size()) + rcSize,
+ TotalSize: int64(proto.Size(rs)) + rcSize,
closeC: make(chan bool, 1),
}
}
@@ -48,11 +50,11 @@ func NewMessage(rs raftpb.Message, rc io.ReadCloser, rcSize int64) *Message {
// CloseNotify returns a channel that receives a single value
// when the message sent is finished. true indicates the sent
// is successful.
-func (m Message) CloseNotify() <-chan bool {
+func (m *Message) CloseNotify() <-chan bool {
return m.closeC
}
-func (m Message) CloseWithError(err error) {
+func (m *Message) CloseWithError(err error) {
if cerr := m.ReadCloser.Close(); cerr != nil {
err = cerr
}
diff --git a/server/etcdserver/api/snap/snappb/snap.pb.go b/server/etcdserver/api/snap/snappb/snap.pb.go
index ff9d39c7ec25..ba3c20f86e06 100644
--- a/server/etcdserver/api/snap/snappb/snap.pb.go
+++ b/server/etcdserver/api/snap/snappb/snap.pb.go
@@ -1,347 +1,133 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: snap.proto
package snappb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Snapshot struct {
- Crc uint32 `protobuf:"varint,1,opt,name=crc" json:"crc"`
- Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Crc *uint32 `protobuf:"varint,1,opt,name=crc" json:"crc,omitempty"`
+ Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Snapshot) Reset() { *m = Snapshot{} }
-func (m *Snapshot) String() string { return proto.CompactTextString(m) }
-func (*Snapshot) ProtoMessage() {}
-func (*Snapshot) Descriptor() ([]byte, []int) {
- return fileDescriptor_f2e3c045ebf84d00, []int{0}
+func (x *Snapshot) Reset() {
+ *x = Snapshot{}
+ mi := &file_snap_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Snapshot) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Snapshot) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Snapshot.Merge(m, src)
-}
-func (m *Snapshot) XXX_Size() int {
- return m.Size()
-}
-func (m *Snapshot) XXX_DiscardUnknown() {
- xxx_messageInfo_Snapshot.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Snapshot proto.InternalMessageInfo
-func init() {
- proto.RegisterType((*Snapshot)(nil), "snappb.snapshot")
+func (x *Snapshot) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func init() { proto.RegisterFile("snap.proto", fileDescriptor_f2e3c045ebf84d00) }
+func (*Snapshot) ProtoMessage() {}
-var fileDescriptor_f2e3c045ebf84d00 = []byte{
- // 164 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xce, 0x4b, 0x2c,
- 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0xb1, 0x0b, 0x92, 0xa4, 0x44, 0xd2, 0xf3,
- 0xd3, 0xf3, 0xc1, 0x42, 0xfa, 0x20, 0x16, 0x44, 0x56, 0xc9, 0x8c, 0x8b, 0x03, 0x24, 0x5f, 0x9c,
- 0x91, 0x5f, 0x22, 0x24, 0xc6, 0xc5, 0x9c, 0x5c, 0x94, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xeb,
- 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x48, 0x40, 0x48, 0x88, 0x8b, 0x25, 0x25, 0xb1, 0x24,
- 0x51, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xcc, 0x76, 0xf2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1,
- 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf1, 0x58, 0x8e,
- 0x21, 0xca, 0x24, 0x3d, 0x5f, 0x2f, 0xb5, 0x24, 0x39, 0x45, 0x2f, 0x33, 0x5f, 0x1f, 0x44, 0xeb,
- 0x17, 0xa7, 0x16, 0x95, 0xa5, 0x16, 0xe9, 0x97, 0x19, 0x83, 0xb9, 0x50, 0x5e, 0x62, 0x41, 0xa6,
- 0x3e, 0xc8, 0x52, 0x7d, 0x88, 0xcb, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8d, 0x65, 0xd9, 0x03,
- 0xae, 0x00, 0x00, 0x00,
-}
-
-func (m *Snapshot) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Snapshot) ProtoReflect() protoreflect.Message {
+ mi := &file_snap_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return dAtA[:n], nil
+ return mi.MessageOf(x)
}
-func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+// Deprecated: Use Snapshot.ProtoReflect.Descriptor instead.
+func (*Snapshot) Descriptor() ([]byte, []int) {
+ return file_snap_proto_rawDescGZIP(), []int{0}
}
-func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Data != nil {
- i -= len(m.Data)
- copy(dAtA[i:], m.Data)
- i = encodeVarintSnap(dAtA, i, uint64(len(m.Data)))
- i--
- dAtA[i] = 0x12
+func (x *Snapshot) GetCrc() uint32 {
+ if x != nil && x.Crc != nil {
+ return *x.Crc
}
- i = encodeVarintSnap(dAtA, i, uint64(m.Crc))
- i--
- dAtA[i] = 0x8
- return len(dAtA) - i, nil
+ return 0
}
-func encodeVarintSnap(dAtA []byte, offset int, v uint64) int {
- offset -= sovSnap(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
+func (x *Snapshot) GetData() []byte {
+ if x != nil {
+ return x.Data
}
- dAtA[offset] = uint8(v)
- return base
+ return nil
}
-func (m *Snapshot) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovSnap(uint64(m.Crc))
- if m.Data != nil {
- l = len(m.Data)
- n += 1 + l + sovSnap(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+
+var File_snap_proto protoreflect.FileDescriptor
+
+const file_snap_proto_rawDesc = "" +
+ "\n" +
+ "\n" +
+ "snap.proto\x12\x06snappb\"0\n" +
+ "\bsnapshot\x12\x10\n" +
+ "\x03crc\x18\x01 \x01(\rR\x03crc\x12\x12\n" +
+ "\x04data\x18\x02 \x01(\fR\x04dataB6Z4go.etcd.io/etcd/server/v3/etcdserver/api/snap/snappb"
+
+var (
+ file_snap_proto_rawDescOnce sync.Once
+ file_snap_proto_rawDescData []byte
+)
+
+func file_snap_proto_rawDescGZIP() []byte {
+ file_snap_proto_rawDescOnce.Do(func() {
+ file_snap_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_snap_proto_rawDesc), len(file_snap_proto_rawDesc)))
+ })
+ return file_snap_proto_rawDescData
}
-func sovSnap(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
+var file_snap_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_snap_proto_goTypes = []any{
+ (*Snapshot)(nil), // 0: snappb.snapshot
}
-func sozSnap(x uint64) (n int) {
- return sovSnap(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+var file_snap_proto_depIdxs = []int32{
+ 0, // [0:0] is the sub-list for method output_type
+ 0, // [0:0] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
}
-func (m *Snapshot) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: snapshot: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: snapshot: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
- }
- m.Crc = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Crc |= uint32(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthSnap
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthSnap
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...)
- if m.Data == nil {
- m.Data = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipSnap(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthSnap
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func init() { file_snap_proto_init() }
+func file_snap_proto_init() {
+ if File_snap_proto != nil {
+ return
}
- return nil
-}
-func skipSnap(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowSnap
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthSnap
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupSnap
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthSnap
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_snap_proto_rawDesc), len(file_snap_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_snap_proto_goTypes,
+ DependencyIndexes: file_snap_proto_depIdxs,
+ MessageInfos: file_snap_proto_msgTypes,
+ }.Build()
+ File_snap_proto = out.File
+ file_snap_proto_goTypes = nil
+ file_snap_proto_depIdxs = nil
}
-
-var (
- ErrInvalidLengthSnap = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowSnap = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupSnap = fmt.Errorf("proto: unexpected end of group")
-)
diff --git a/server/etcdserver/api/snap/snappb/snap.proto b/server/etcdserver/api/snap/snappb/snap.proto
index 0a74744e4b2d..7e97db993002 100644
--- a/server/etcdserver/api/snap/snappb/snap.proto
+++ b/server/etcdserver/api/snap/snappb/snap.proto
@@ -1,16 +1,9 @@
syntax = "proto2";
package snappb;
-import "gogoproto/gogo.proto";
-
option go_package = "go.etcd.io/etcd/server/v3/etcdserver/api/snap/snappb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-
message snapshot {
- optional uint32 crc = 1 [(gogoproto.nullable) = false];
+ optional uint32 crc = 1;
optional bytes data = 2;
}
diff --git a/server/etcdserver/api/snap/snapshotter.go b/server/etcdserver/api/snap/snapshotter.go
index 0de6f9b91ea5..5b5c365aea5c 100644
--- a/server/etcdserver/api/snap/snapshotter.go
+++ b/server/etcdserver/api/snap/snapshotter.go
@@ -26,6 +26,7 @@ import (
"time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/verify"
pioutil "go.etcd.io/etcd/pkg/v3/ioutil"
@@ -65,21 +66,22 @@ func New(lg *zap.Logger, dir string) *Snapshotter {
}
}
-func (s *Snapshotter) SaveSnap(snapshot raftpb.Snapshot) error {
+// TODO: change signature of IsEmptySnap to accept a pointer
+func (s *Snapshotter) SaveSnap(snapshot *raftpb.Snapshot) error {
if raft.IsEmptySnap(snapshot) {
return nil
}
- return s.save(&snapshot)
+ return s.save(snapshot)
}
func (s *Snapshotter) save(snapshot *raftpb.Snapshot) error {
start := time.Now()
- fname := fmt.Sprintf("%016x-%016x%s", snapshot.Metadata.Term, snapshot.Metadata.Index, snapSuffix)
- b := pbutil.MustMarshal(snapshot)
+ fname := fmt.Sprintf("%016x-%016x%s", snapshot.Metadata.GetTerm(), snapshot.Metadata.GetIndex(), snapSuffix)
+ b := pbutil.MustMarshalMessage(snapshot)
crc := crc32.Update(0, crcTable, b)
- snap := snappb.Snapshot{Crc: crc, Data: b}
- d, err := snap.Marshal()
+ snap := snappb.Snapshot{Crc: &crc, Data: b}
+ d, err := proto.Marshal(&snap)
if err != nil {
return err
}
@@ -110,11 +112,11 @@ func (s *Snapshotter) Load() (*raftpb.Snapshot, error) {
}
// LoadNewestAvailable loads the newest snapshot available that is in walSnaps.
-func (s *Snapshotter) LoadNewestAvailable(walSnaps []walpb.Snapshot) (*raftpb.Snapshot, error) {
+func (s *Snapshotter) LoadNewestAvailable(walSnaps []*walpb.Snapshot) (*raftpb.Snapshot, error) {
return s.loadMatching(func(snapshot *raftpb.Snapshot) bool {
m := snapshot.Metadata
for i := len(walSnaps) - 1; i >= 0; i-- {
- if m.Term == walSnaps[i].Term && m.Index == walSnaps[i].Index {
+ if m.GetTerm() == walSnaps[i].GetTerm() && m.GetIndex() == walSnaps[i].GetIndex() {
return true
}
}
@@ -167,28 +169,28 @@ func Read(lg *zap.Logger, snapname string) (*raftpb.Snapshot, error) {
}
var serializedSnap snappb.Snapshot
- if err = serializedSnap.Unmarshal(b); err != nil {
+ if err = proto.Unmarshal(b, &serializedSnap); err != nil {
lg.Warn("failed to unmarshal snappb.Snapshot", zap.String("path", snapname), zap.Error(err))
return nil, err
}
- if len(serializedSnap.Data) == 0 || serializedSnap.Crc == 0 {
+ if len(serializedSnap.Data) == 0 || serializedSnap.GetCrc() == 0 {
lg.Warn("failed to read empty snapshot data", zap.String("path", snapname))
return nil, ErrEmptySnapshot
}
crc := crc32.Update(0, crcTable, serializedSnap.Data)
- if crc != serializedSnap.Crc {
+ if crc != serializedSnap.GetCrc() {
lg.Warn("snap file is corrupt",
zap.String("path", snapname),
- zap.Uint32("prev-crc", serializedSnap.Crc),
+ zap.Uint32("prev-crc", serializedSnap.GetCrc()),
zap.Uint32("new-crc", crc),
)
return nil, ErrCRCMismatch
}
var snap raftpb.Snapshot
- if err = snap.Unmarshal(serializedSnap.Data); err != nil {
+ if err = proto.Unmarshal(serializedSnap.Data, &snap); err != nil {
lg.Warn("failed to unmarshal raftpb.Snapshot", zap.String("path", snapname), zap.Error(err))
return nil, err
}
@@ -252,7 +254,7 @@ func (s *Snapshotter) cleanupSnapdir(filenames []string) (names []string, err er
return names, nil
}
-func (s *Snapshotter) ReleaseSnapDBs(snap raftpb.Snapshot) error {
+func (s *Snapshotter) ReleaseSnapDBs(snap *raftpb.Snapshot) error {
dir, err := os.Open(s.dir)
if err != nil {
return err
@@ -270,7 +272,7 @@ func (s *Snapshotter) ReleaseSnapDBs(snap raftpb.Snapshot) error {
s.lg.Error("failed to parse index from filename", zap.String("path", filename), zap.String("error", err.Error()))
continue
}
- if index < snap.Metadata.Index {
+ if index < snap.Metadata.GetIndex() {
s.lg.Info("found orphaned .snap.db file; deleting", zap.String("path", filename))
if rmErr := os.Remove(filepath.Join(s.dir, filename)); rmErr != nil && !os.IsNotExist(rmErr) {
s.lg.Error("failed to remove orphaned .snap.db file", zap.String("path", filename), zap.String("error", rmErr.Error()))
diff --git a/server/etcdserver/api/snap/snapshotter_test.go b/server/etcdserver/api/snap/snapshotter_test.go
index 51d0ac0443a2..85dbbc1824b4 100644
--- a/server/etcdserver/api/snap/snapshotter_test.go
+++ b/server/etcdserver/api/snap/snapshotter_test.go
@@ -24,6 +24,7 @@ import (
"testing"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
@@ -32,12 +33,12 @@ import (
var testSnap = &raftpb.Snapshot{
Data: []byte("some snapshot"),
- Metadata: raftpb.SnapshotMetadata{
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ ConfState: &raftpb.ConfState{
Voters: []uint64{1, 2, 3},
},
- Index: 1,
- Term: 1,
+ Index: new(uint64(1)),
+ Term: new(uint64(1)),
},
}
@@ -58,7 +59,7 @@ func TestSaveAndLoad(t *testing.T) {
if err != nil {
t.Errorf("err = %v, want nil", err)
}
- if !reflect.DeepEqual(g, testSnap) {
+ if !proto.Equal(g, testSnap) {
t.Errorf("snap = %#v, want %#v", g, testSnap)
}
}
@@ -110,7 +111,7 @@ func TestFailback(t *testing.T) {
if err != nil {
t.Errorf("err = %v, want nil", err)
}
- if !reflect.DeepEqual(g, testSnap) {
+ if !proto.Equal(g, testSnap) {
t.Errorf("snap = %#v, want %#v", g, testSnap)
}
if f, err := os.Open(filepath.Join(dir, large) + ".broken"); err != nil {
@@ -162,35 +163,35 @@ func TestLoadNewestSnap(t *testing.T) {
t.Fatal(err)
}
- newSnap := *testSnap
- newSnap.Metadata.Index = 5
- err = ss.save(&newSnap)
+ newSnap := proto.Clone(testSnap).(*raftpb.Snapshot)
+ newSnap.Metadata.Index = new(uint64(5))
+ err = ss.save(newSnap)
if err != nil {
t.Fatal(err)
}
cases := []struct {
name string
- availableWALSnaps []walpb.Snapshot
+ availableWALSnaps []*walpb.Snapshot
expected *raftpb.Snapshot
}{
{
name: "load-newest",
- expected: &newSnap,
+ expected: newSnap,
},
{
name: "loadnewestavailable-newest",
- availableWALSnaps: []walpb.Snapshot{{Index: 0, Term: 0}, {Index: 1, Term: 1}, {Index: 5, Term: 1}},
- expected: &newSnap,
+ availableWALSnaps: []*walpb.Snapshot{{Index: new(uint64(0)), Term: new(uint64(0))}, {Index: new(uint64(1)), Term: new(uint64(1))}, {Index: new(uint64(5)), Term: new(uint64(1))}},
+ expected: newSnap,
},
{
name: "loadnewestavailable-newest-unsorted",
- availableWALSnaps: []walpb.Snapshot{{Index: 5, Term: 1}, {Index: 1, Term: 1}, {Index: 0, Term: 0}},
- expected: &newSnap,
+ availableWALSnaps: []*walpb.Snapshot{{Index: new(uint64(5)), Term: new(uint64(1))}, {Index: new(uint64(1)), Term: new(uint64(1))}, {Index: new(uint64(0)), Term: new(uint64(0))}},
+ expected: newSnap,
},
{
name: "loadnewestavailable-previous",
- availableWALSnaps: []walpb.Snapshot{{Index: 0, Term: 0}, {Index: 1, Term: 1}},
+ availableWALSnaps: []*walpb.Snapshot{{Index: new(uint64(0)), Term: new(uint64(0))}, {Index: new(uint64(1)), Term: new(uint64(1))}},
expected: testSnap,
},
}
@@ -206,7 +207,7 @@ func TestLoadNewestSnap(t *testing.T) {
if err != nil {
t.Errorf("err = %v, want nil", err)
}
- if !reflect.DeepEqual(g, tc.expected) {
+ if !proto.Equal(g, tc.expected) {
t.Errorf("snap = %#v, want %#v", g, tc.expected)
}
})
@@ -286,7 +287,7 @@ func TestReleaseSnapDBs(t *testing.T) {
ss := New(zaptest.NewLogger(t), dir)
- if err := ss.ReleaseSnapDBs(raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 300}}); err != nil {
+ if err := ss.ReleaseSnapDBs(&raftpb.Snapshot{Metadata: &raftpb.SnapshotMetadata{Index: new(uint64(300))}}); err != nil {
t.Fatal(err)
}
diff --git a/server/etcdserver/api/v2error/error_test.go b/server/etcdserver/api/v2error/error_test.go
index 7590c1166d4f..da6a45ba5218 100644
--- a/server/etcdserver/api/v2error/error_test.go
+++ b/server/etcdserver/api/v2error/error_test.go
@@ -42,8 +42,8 @@ func TestErrorWriteTo(t *testing.T) {
"X-Etcd-Index": {"1"},
})
- if !reflect.DeepEqual(wheader, rr.HeaderMap) {
- t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader)
+ if !reflect.DeepEqual(wheader, rr.HeaderMap) { //nolint:staticcheck // TODO: remove for a supported version
+ t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader) //nolint:staticcheck // TODO: remove for a supported version
}
}
}
diff --git a/server/etcdserver/api/v2store/event_test.go b/server/etcdserver/api/v2store/event_test.go
index 11553e035a07..e390778eb9c3 100644
--- a/server/etcdserver/api/v2store/event_test.go
+++ b/server/etcdserver/api/v2store/event_test.go
@@ -85,7 +85,7 @@ func TestScanHistory(t *testing.T) {
e, _ = eh.scan("/foo/bar", true, 7)
if e != nil {
- t.Fatalf("bad index shoud reuturn nil")
+ t.Fatalf("bad index should reuturn nil")
}
}
diff --git a/server/etcdserver/api/v2store/store_ttl_test.go b/server/etcdserver/api/v2store/store_ttl_test.go
index a4569cc3114b..aa98b4505e7b 100644
--- a/server/etcdserver/api/v2store/store_ttl_test.go
+++ b/server/etcdserver/api/v2store/store_ttl_test.go
@@ -132,7 +132,7 @@ func TestStoreUpdateDirTTL(t *testing.T) {
assert.False(t, e.Node.Dir)
assert.Equal(t, eidx, e.EtcdIndex)
e, _ = s.Get("/foo/bar", false, false)
- assert.Equal(t, "", *e.Node.Value)
+ assert.Empty(t, *e.Node.Value)
assert.Equal(t, eidx, e.EtcdIndex)
fc.Advance(600 * time.Millisecond)
diff --git a/server/etcdserver/api/v3compactor/periodic_test.go b/server/etcdserver/api/v3compactor/periodic_test.go
index 5604dabc5ef9..e8658f420fa2 100644
--- a/server/etcdserver/api/v3compactor/periodic_test.go
+++ b/server/etcdserver/api/v3compactor/periodic_test.go
@@ -16,6 +16,7 @@ package v3compactor
import (
"errors"
+ "fmt"
"reflect"
"testing"
"time"
@@ -49,7 +50,7 @@ func TestPeriodicHourly(t *testing.T) {
}
// very first compaction
- a, err := compactable.Wait(1)
+ a, err := waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -67,7 +68,7 @@ func TestPeriodicHourly(t *testing.T) {
fc.Advance(tb.getRetryInterval())
}
- a, err = compactable.Wait(1)
+ a, err = waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -100,7 +101,7 @@ func TestPeriodicMinutes(t *testing.T) {
}
// very first compaction
- a, err := compactable.Wait(1)
+ a, err := waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -117,7 +118,7 @@ func TestPeriodicMinutes(t *testing.T) {
fc.Advance(tb.getRetryInterval())
}
- a, err := compactable.Wait(1)
+ a, err := waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -162,7 +163,7 @@ func TestPeriodicPause(t *testing.T) {
fc.Advance(tb.getRetryInterval())
// T=3h6m
- a, err := compactable.Wait(1)
+ a, err := waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -197,7 +198,7 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
}
// very first compaction
- a, err := compactable.Wait(1)
+ a, err := waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -228,7 +229,7 @@ func TestPeriodicSkipRevNotChange(t *testing.T) {
fc.Advance(tb.getRetryInterval())
}
- a, err = compactable.Wait(1)
+ a, err = waitWithRetry(t, compactable)
if err != nil {
t.Fatal(err)
}
@@ -244,3 +245,23 @@ func waitOneAction(t *testing.T, r testutil.Recorder) {
t.Errorf("expect 1 action, got %v instead", len(actions))
}
}
+
+func waitWithRetry(t *testing.T, compactable *fakeCompactable) ([]testutil.Action, error) {
+ t.Helper()
+
+ var lastErr error
+ var actions []testutil.Action
+
+ expectedActions, maxRetries := 1, 5
+ for retry := 0; retry < maxRetries; retry++ {
+ actions, lastErr = compactable.Wait(expectedActions)
+ if lastErr == nil || len(actions) >= expectedActions {
+ return actions, nil
+ }
+ // Exponential backoff
+ backoffTime := time.Duration(10*(1< 0 {
- i -= len(m.Value)
- copy(dAtA[i:], m.Value)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Value)))
- i--
- dAtA[i] = 0x1a
- }
- if m.Lease != 0 {
- i = encodeVarintV3Election(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x10
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *ResignRequest) Reset() {
+ *x = ResignRequest{}
+ mi := &file_v3election_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *CampaignResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *ResignRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *CampaignResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*ResignRequest) ProtoMessage() {}
-func (m *CampaignResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Leader != nil {
- {
- size, err := m.Leader.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
+func (x *ResignRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_v3election_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x12
+ return ms
}
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *LeaderKey) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LeaderKey) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+ return mi.MessageOf(x)
}
-func (m *LeaderKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Lease != 0 {
- i = encodeVarintV3Election(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x20
- }
- if m.Rev != 0 {
- i = encodeVarintV3Election(dAtA, i, uint64(m.Rev))
- i--
- dAtA[i] = 0x18
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0x12
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+// Deprecated: Use ResignRequest.ProtoReflect.Descriptor instead.
+func (*ResignRequest) Descriptor() ([]byte, []int) {
+ return file_v3election_proto_rawDescGZIP(), []int{5}
}
-func (m *LeaderRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *ResignRequest) GetLeader() *LeaderKey {
+ if x != nil {
+ return x.Leader
}
- return dAtA[:n], nil
+ return nil
}
-func (m *LeaderRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type ResignResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *etcdserverpb.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaderRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *ResignResponse) Reset() {
+ *x = ResignResponse{}
+ mi := &file_v3election_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaderResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *ResignResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaderResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*ResignResponse) ProtoMessage() {}
-func (m *LeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Kv != nil {
- {
- size, err := m.Kv.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
+func (x *ResignResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_v3election_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *ResignRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *ResignRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Leader != nil {
- {
- size, err := m.Leader.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func (m *ResignResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+ return ms
}
- return dAtA[:n], nil
+ return mi.MessageOf(x)
}
-func (m *ResignResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *ResignResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+// Deprecated: Use ResignResponse.ProtoReflect.Descriptor instead.
+func (*ResignResponse) Descriptor() ([]byte, []int) {
+ return file_v3election_proto_rawDescGZIP(), []int{6}
}
-func (m *ProclaimRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *ResignResponse) GetHeader() *etcdserverpb.ResponseHeader {
+ if x != nil {
+ return x.Header
}
- return dAtA[:n], nil
+ return nil
}
-func (m *ProclaimRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type ProclaimRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // leader is the leadership hold on the election.
+ Leader *LeaderKey `protobuf:"bytes,1,opt,name=leader,proto3" json:"leader,omitempty"`
+ // value is an update meant to overwrite the leader's current value.
+ Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *ProclaimRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Value) > 0 {
- i -= len(m.Value)
- copy(dAtA[i:], m.Value)
- i = encodeVarintV3Election(dAtA, i, uint64(len(m.Value)))
- i--
- dAtA[i] = 0x12
- }
- if m.Leader != nil {
- {
- size, err := m.Leader.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *ProclaimRequest) Reset() {
+ *x = ProclaimRequest{}
+ mi := &file_v3election_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *ProclaimResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *ProclaimRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *ProclaimResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*ProclaimRequest) ProtoMessage() {}
-func (m *ProclaimResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Election(dAtA, i, uint64(size))
+func (x *ProclaimRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_v3election_proto_msgTypes[7]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
-
-func encodeVarintV3Election(dAtA []byte, offset int, v uint64) int {
- offset -= sovV3Election(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *CampaignRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.Lease != 0 {
- n += 1 + sovV3Election(uint64(m.Lease))
- }
- l = len(m.Value)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *CampaignResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.Leader != nil {
- l = m.Leader.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaderKey) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.Rev != 0 {
- n += 1 + sovV3Election(uint64(m.Rev))
- }
- if m.Lease != 0 {
- n += 1 + sovV3Election(uint64(m.Lease))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaderRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *LeaderResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.Kv != nil {
- l = m.Kv.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *ResignRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Leader != nil {
- l = m.Leader.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *ResignResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
+ return ms
}
- return n
+ return mi.MessageOf(x)
}
-func (m *ProclaimRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Leader != nil {
- l = m.Leader.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- l = len(m.Value)
- if l > 0 {
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use ProclaimRequest.ProtoReflect.Descriptor instead.
+func (*ProclaimRequest) Descriptor() ([]byte, []int) {
+ return file_v3election_proto_rawDescGZIP(), []int{7}
}
-func (m *ProclaimResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Election(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func sovV3Election(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozV3Election(x uint64) (n int) {
- return sovV3Election(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *CampaignRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: CampaignRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: CampaignRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- m.Lease = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Lease |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
- if m.Value == nil {
- m.Value = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *ProclaimRequest) GetLeader() *LeaderKey {
+ if x != nil {
+ return x.Leader
}
return nil
}
-func (m *CampaignResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: CampaignResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: CampaignResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Leader == nil {
- m.Leader = &LeaderKey{}
- }
- if err := m.Leader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *ProclaimRequest) GetValue() []byte {
+ if x != nil {
+ return x.Value
}
return nil
}
-func (m *LeaderKey) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaderKey: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaderKey: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Rev", wireType)
- }
- m.Rev = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Rev |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 4:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- m.Lease = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Lease |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+type ProclaimResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *etcdserverpb.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaderRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaderRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaderRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *ProclaimResponse) Reset() {
+ *x = ProclaimResponse{}
+ mi := &file_v3election_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaderResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaderResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaderResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Kv == nil {
- m.Kv = &mvccpb.KeyValue{}
- }
- if err := m.Kv.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *ProclaimResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *ResignRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ResignRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ResignRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Leader == nil {
- m.Leader = &LeaderKey{}
- }
- if err := m.Leader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *ResignResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ResignResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ResignResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
+func (*ProclaimResponse) ProtoMessage() {}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func (m *ProclaimRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ProclaimRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ProclaimRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Leader == nil {
- m.Leader = &LeaderKey{}
- }
- if err := m.Leader.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
- if m.Value == nil {
- m.Value = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
+func (x *ProclaimResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_v3election_proto_msgTypes[8]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
+ return mi.MessageOf(x)
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+// Deprecated: Use ProclaimResponse.ProtoReflect.Descriptor instead.
+func (*ProclaimResponse) Descriptor() ([]byte, []int) {
+ return file_v3election_proto_rawDescGZIP(), []int{8}
}
-func (m *ProclaimResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: ProclaimResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: ProclaimResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Election
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Election
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Election(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Election
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *ProclaimResponse) GetHeader() *etcdserverpb.ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func skipV3Election(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Election
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthV3Election
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupV3Election
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthV3Election
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+
+var File_v3election_proto protoreflect.FileDescriptor
+
+const file_v3election_proto_rawDesc = "" +
+ "\n" +
+ "\x10v3election.proto\x12\fv3electionpb\x1a\x1fetcd/api/etcdserverpb/rpc.proto\x1a\x18etcd/api/mvccpb/kv.proto\x1a\x1cgoogle/api/annotations.proto\"Q\n" +
+ "\x0fCampaignRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\x12\x14\n" +
+ "\x05lease\x18\x02 \x01(\x03R\x05lease\x12\x14\n" +
+ "\x05value\x18\x03 \x01(\fR\x05value\"y\n" +
+ "\x10CampaignResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12/\n" +
+ "\x06leader\x18\x02 \x01(\v2\x17.v3electionpb.LeaderKeyR\x06leader\"Y\n" +
+ "\tLeaderKey\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\x12\x10\n" +
+ "\x03key\x18\x02 \x01(\fR\x03key\x12\x10\n" +
+ "\x03rev\x18\x03 \x01(\x03R\x03rev\x12\x14\n" +
+ "\x05lease\x18\x04 \x01(\x03R\x05lease\"#\n" +
+ "\rLeaderRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\"h\n" +
+ "\x0eLeaderResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12 \n" +
+ "\x02kv\x18\x02 \x01(\v2\x10.mvccpb.KeyValueR\x02kv\"@\n" +
+ "\rResignRequest\x12/\n" +
+ "\x06leader\x18\x01 \x01(\v2\x17.v3electionpb.LeaderKeyR\x06leader\"F\n" +
+ "\x0eResignResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\"X\n" +
+ "\x0fProclaimRequest\x12/\n" +
+ "\x06leader\x18\x01 \x01(\v2\x17.v3electionpb.LeaderKeyR\x06leader\x12\x14\n" +
+ "\x05value\x18\x02 \x01(\fR\x05value\"H\n" +
+ "\x10ProclaimResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header2\x97\x04\n" +
+ "\bElection\x12k\n" +
+ "\bCampaign\x12\x1d.v3electionpb.CampaignRequest\x1a\x1e.v3electionpb.CampaignResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/v3/election/campaign\x12k\n" +
+ "\bProclaim\x12\x1d.v3electionpb.ProclaimRequest\x1a\x1e.v3electionpb.ProclaimResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/v3/election/proclaim\x12c\n" +
+ "\x06Leader\x12\x1b.v3electionpb.LeaderRequest\x1a\x1c.v3electionpb.LeaderResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/v3/election/leader\x12g\n" +
+ "\aObserve\x12\x1b.v3electionpb.LeaderRequest\x1a\x1c.v3electionpb.LeaderResponse\"\x1f\x82\xd3\xe4\x93\x02\x19:\x01*\"\x14/v3/election/observe0\x01\x12c\n" +
+ "\x06Resign\x12\x1b.v3electionpb.ResignRequest\x1a\x1c.v3electionpb.ResignResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/v3/election/resignBBZ@go.etcd.io/etcd/server/v3/etcdserver/api/v3election/v3electionpbb\x06proto3"
var (
- ErrInvalidLengthV3Election = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowV3Election = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupV3Election = fmt.Errorf("proto: unexpected end of group")
+ file_v3election_proto_rawDescOnce sync.Once
+ file_v3election_proto_rawDescData []byte
)
+
+func file_v3election_proto_rawDescGZIP() []byte {
+ file_v3election_proto_rawDescOnce.Do(func() {
+ file_v3election_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_v3election_proto_rawDesc), len(file_v3election_proto_rawDesc)))
+ })
+ return file_v3election_proto_rawDescData
+}
+
+var file_v3election_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_v3election_proto_goTypes = []any{
+ (*CampaignRequest)(nil), // 0: v3electionpb.CampaignRequest
+ (*CampaignResponse)(nil), // 1: v3electionpb.CampaignResponse
+ (*LeaderKey)(nil), // 2: v3electionpb.LeaderKey
+ (*LeaderRequest)(nil), // 3: v3electionpb.LeaderRequest
+ (*LeaderResponse)(nil), // 4: v3electionpb.LeaderResponse
+ (*ResignRequest)(nil), // 5: v3electionpb.ResignRequest
+ (*ResignResponse)(nil), // 6: v3electionpb.ResignResponse
+ (*ProclaimRequest)(nil), // 7: v3electionpb.ProclaimRequest
+ (*ProclaimResponse)(nil), // 8: v3electionpb.ProclaimResponse
+ (*etcdserverpb.ResponseHeader)(nil), // 9: etcdserverpb.ResponseHeader
+ (*mvccpb.KeyValue)(nil), // 10: mvccpb.KeyValue
+}
+var file_v3election_proto_depIdxs = []int32{
+ 9, // 0: v3electionpb.CampaignResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 2, // 1: v3electionpb.CampaignResponse.leader:type_name -> v3electionpb.LeaderKey
+ 9, // 2: v3electionpb.LeaderResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 10, // 3: v3electionpb.LeaderResponse.kv:type_name -> mvccpb.KeyValue
+ 2, // 4: v3electionpb.ResignRequest.leader:type_name -> v3electionpb.LeaderKey
+ 9, // 5: v3electionpb.ResignResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 2, // 6: v3electionpb.ProclaimRequest.leader:type_name -> v3electionpb.LeaderKey
+ 9, // 7: v3electionpb.ProclaimResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 0, // 8: v3electionpb.Election.Campaign:input_type -> v3electionpb.CampaignRequest
+ 7, // 9: v3electionpb.Election.Proclaim:input_type -> v3electionpb.ProclaimRequest
+ 3, // 10: v3electionpb.Election.Leader:input_type -> v3electionpb.LeaderRequest
+ 3, // 11: v3electionpb.Election.Observe:input_type -> v3electionpb.LeaderRequest
+ 5, // 12: v3electionpb.Election.Resign:input_type -> v3electionpb.ResignRequest
+ 1, // 13: v3electionpb.Election.Campaign:output_type -> v3electionpb.CampaignResponse
+ 8, // 14: v3electionpb.Election.Proclaim:output_type -> v3electionpb.ProclaimResponse
+ 4, // 15: v3electionpb.Election.Leader:output_type -> v3electionpb.LeaderResponse
+ 4, // 16: v3electionpb.Election.Observe:output_type -> v3electionpb.LeaderResponse
+ 6, // 17: v3electionpb.Election.Resign:output_type -> v3electionpb.ResignResponse
+ 13, // [13:18] is the sub-list for method output_type
+ 8, // [8:13] is the sub-list for method input_type
+ 8, // [8:8] is the sub-list for extension type_name
+ 8, // [8:8] is the sub-list for extension extendee
+ 0, // [0:8] is the sub-list for field type_name
+}
+
+func init() { file_v3election_proto_init() }
+func file_v3election_proto_init() {
+ if File_v3election_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_v3election_proto_rawDesc), len(file_v3election_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 9,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_v3election_proto_goTypes,
+ DependencyIndexes: file_v3election_proto_depIdxs,
+ MessageInfos: file_v3election_proto_msgTypes,
+ }.Build()
+ File_v3election_proto = out.File
+ file_v3election_proto_goTypes = nil
+ file_v3election_proto_depIdxs = nil
+}
diff --git a/server/etcdserver/api/v3election/v3electionpb/v3election.proto b/server/etcdserver/api/v3election/v3electionpb/v3election.proto
index 6042776b90cb..6f0488b02304 100644
--- a/server/etcdserver/api/v3election/v3electionpb/v3election.proto
+++ b/server/etcdserver/api/v3election/v3electionpb/v3election.proto
@@ -1,7 +1,6 @@
syntax = "proto3";
package v3electionpb;
-import "gogoproto/gogo.proto";
import "etcd/api/etcdserverpb/rpc.proto";
import "etcd/api/mvccpb/kv.proto";
@@ -10,9 +9,6 @@ import "google/api/annotations.proto";
option go_package = "go.etcd.io/etcd/server/v3/etcdserver/api/v3election/v3electionpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-
// The election service exposes client-side election facilities as a gRPC interface.
service Election {
// Campaign waits to acquire leadership in an election, returning a LeaderKey
diff --git a/server/etcdserver/api/v3election/v3electionpb/v3election_grpc.pb.go b/server/etcdserver/api/v3election/v3electionpb/v3election_grpc.pb.go
new file mode 100644
index 000000000000..72d3da7f4879
--- /dev/null
+++ b/server/etcdserver/api/v3election/v3electionpb/v3election_grpc.pb.go
@@ -0,0 +1,302 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.6.2
+// - protoc v3.20.3
+// source: v3election.proto
+
+package v3electionpb
+
+import (
+ context "context"
+
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ Election_Campaign_FullMethodName = "/v3electionpb.Election/Campaign"
+ Election_Proclaim_FullMethodName = "/v3electionpb.Election/Proclaim"
+ Election_Leader_FullMethodName = "/v3electionpb.Election/Leader"
+ Election_Observe_FullMethodName = "/v3electionpb.Election/Observe"
+ Election_Resign_FullMethodName = "/v3electionpb.Election/Resign"
+)
+
+// ElectionClient is the client API for Election service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// The election service exposes client-side election facilities as a gRPC interface.
+type ElectionClient interface {
+ // Campaign waits to acquire leadership in an election, returning a LeaderKey
+ // representing the leadership if successful. The LeaderKey can then be used
+ // to issue new values on the election, transactionally guard API requests on
+ // leadership still being held, and resign from the election.
+ Campaign(ctx context.Context, in *CampaignRequest, opts ...grpc.CallOption) (*CampaignResponse, error)
+ // Proclaim updates the leader's posted value with a new value.
+ Proclaim(ctx context.Context, in *ProclaimRequest, opts ...grpc.CallOption) (*ProclaimResponse, error)
+ // Leader returns the current election proclamation, if any.
+ Leader(ctx context.Context, in *LeaderRequest, opts ...grpc.CallOption) (*LeaderResponse, error)
+ // Observe streams election proclamations in-order as made by the election's
+ // elected leaders.
+ Observe(ctx context.Context, in *LeaderRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LeaderResponse], error)
+ // Resign releases election leadership so other campaigners may acquire
+ // leadership on the election.
+ Resign(ctx context.Context, in *ResignRequest, opts ...grpc.CallOption) (*ResignResponse, error)
+}
+
+type electionClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewElectionClient(cc grpc.ClientConnInterface) ElectionClient {
+ return &electionClient{cc}
+}
+
+func (c *electionClient) Campaign(ctx context.Context, in *CampaignRequest, opts ...grpc.CallOption) (*CampaignResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(CampaignResponse)
+ err := c.cc.Invoke(ctx, Election_Campaign_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *electionClient) Proclaim(ctx context.Context, in *ProclaimRequest, opts ...grpc.CallOption) (*ProclaimResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(ProclaimResponse)
+ err := c.cc.Invoke(ctx, Election_Proclaim_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *electionClient) Leader(ctx context.Context, in *LeaderRequest, opts ...grpc.CallOption) (*LeaderResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LeaderResponse)
+ err := c.cc.Invoke(ctx, Election_Leader_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *electionClient) Observe(ctx context.Context, in *LeaderRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LeaderResponse], error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ stream, err := c.cc.NewStream(ctx, &Election_ServiceDesc.Streams[0], Election_Observe_FullMethodName, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &grpc.GenericClientStream[LeaderRequest, LeaderResponse]{ClientStream: stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Election_ObserveClient = grpc.ServerStreamingClient[LeaderResponse]
+
+func (c *electionClient) Resign(ctx context.Context, in *ResignRequest, opts ...grpc.CallOption) (*ResignResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(ResignResponse)
+ err := c.cc.Invoke(ctx, Election_Resign_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// ElectionServer is the server API for Election service.
+// All implementations must embed UnimplementedElectionServer
+// for forward compatibility.
+//
+// The election service exposes client-side election facilities as a gRPC interface.
+type ElectionServer interface {
+ // Campaign waits to acquire leadership in an election, returning a LeaderKey
+ // representing the leadership if successful. The LeaderKey can then be used
+ // to issue new values on the election, transactionally guard API requests on
+ // leadership still being held, and resign from the election.
+ Campaign(context.Context, *CampaignRequest) (*CampaignResponse, error)
+ // Proclaim updates the leader's posted value with a new value.
+ Proclaim(context.Context, *ProclaimRequest) (*ProclaimResponse, error)
+ // Leader returns the current election proclamation, if any.
+ Leader(context.Context, *LeaderRequest) (*LeaderResponse, error)
+ // Observe streams election proclamations in-order as made by the election's
+ // elected leaders.
+ Observe(*LeaderRequest, grpc.ServerStreamingServer[LeaderResponse]) error
+ // Resign releases election leadership so other campaigners may acquire
+ // leadership on the election.
+ Resign(context.Context, *ResignRequest) (*ResignResponse, error)
+ mustEmbedUnimplementedElectionServer()
+}
+
+// UnimplementedElectionServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedElectionServer struct{}
+
+func (UnimplementedElectionServer) Campaign(context.Context, *CampaignRequest) (*CampaignResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Campaign not implemented")
+}
+func (UnimplementedElectionServer) Proclaim(context.Context, *ProclaimRequest) (*ProclaimResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Proclaim not implemented")
+}
+func (UnimplementedElectionServer) Leader(context.Context, *LeaderRequest) (*LeaderResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Leader not implemented")
+}
+func (UnimplementedElectionServer) Observe(*LeaderRequest, grpc.ServerStreamingServer[LeaderResponse]) error {
+ return status.Error(codes.Unimplemented, "method Observe not implemented")
+}
+func (UnimplementedElectionServer) Resign(context.Context, *ResignRequest) (*ResignResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Resign not implemented")
+}
+func (UnimplementedElectionServer) mustEmbedUnimplementedElectionServer() {}
+func (UnimplementedElectionServer) testEmbeddedByValue() {}
+
+// UnsafeElectionServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ElectionServer will
+// result in compilation errors.
+type UnsafeElectionServer interface {
+ mustEmbedUnimplementedElectionServer()
+}
+
+func RegisterElectionServer(s grpc.ServiceRegistrar, srv ElectionServer) {
+ // If the following call panics, it indicates UnimplementedElectionServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Election_ServiceDesc, srv)
+}
+
+func _Election_Campaign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(CampaignRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ElectionServer).Campaign(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Election_Campaign_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ElectionServer).Campaign(ctx, req.(*CampaignRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Election_Proclaim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ProclaimRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ElectionServer).Proclaim(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Election_Proclaim_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ElectionServer).Proclaim(ctx, req.(*ProclaimRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Election_Leader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LeaderRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ElectionServer).Leader(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Election_Leader_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ElectionServer).Leader(ctx, req.(*LeaderRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Election_Observe_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(LeaderRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(ElectionServer).Observe(m, &grpc.GenericServerStream[LeaderRequest, LeaderResponse]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type Election_ObserveServer = grpc.ServerStreamingServer[LeaderResponse]
+
+func _Election_Resign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ResignRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ElectionServer).Resign(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Election_Resign_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ElectionServer).Resign(ctx, req.(*ResignRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Election_ServiceDesc is the grpc.ServiceDesc for Election service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Election_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "v3electionpb.Election",
+ HandlerType: (*ElectionServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Campaign",
+ Handler: _Election_Campaign_Handler,
+ },
+ {
+ MethodName: "Proclaim",
+ Handler: _Election_Proclaim_Handler,
+ },
+ {
+ MethodName: "Leader",
+ Handler: _Election_Leader_Handler,
+ },
+ {
+ MethodName: "Resign",
+ Handler: _Election_Resign_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "Observe",
+ Handler: _Election_Observe_Handler,
+ ServerStreams: true,
+ },
+ },
+ Metadata: "v3election.proto",
+}
diff --git a/server/etcdserver/api/v3lock/lock.go b/server/etcdserver/api/v3lock/lock.go
index c8ef56ebaebb..1cf633561513 100644
--- a/server/etcdserver/api/v3lock/lock.go
+++ b/server/etcdserver/api/v3lock/lock.go
@@ -24,10 +24,12 @@ import (
type lockServer struct {
c *clientv3.Client
+ // we want compile errors if new methods are added
+ v3lockpb.UnsafeLockServer
}
func NewLockServer(c *clientv3.Client) v3lockpb.LockServer {
- return &lockServer{c}
+ return &lockServer{c: c}
}
func (ls *lockServer) Lock(ctx context.Context, req *v3lockpb.LockRequest) (*v3lockpb.LockResponse, error) {
diff --git a/server/etcdserver/api/v3lock/v3lockpb/gw/v3lock.pb.gw.go b/server/etcdserver/api/v3lock/v3lockpb/gw/v3lock.pb.gw.go
index 5efb75939c9d..342ea4c57ded 100644
--- a/server/etcdserver/api/v3lock/v3lockpb/gw/v3lock.pb.gw.go
+++ b/server/etcdserver/api/v3lock/v3lockpb/gw/v3lock.pb.gw.go
@@ -9,8 +9,6 @@ It translates gRPC into RESTful JSON APIs.
package gw
import (
- protov1 "github.com/golang/protobuf/proto"
-
"context"
"errors"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3lock/v3lockpb"
@@ -43,11 +41,14 @@ func request_Lock_Lock_0(ctx context.Context, marshaler runtime.Marshaler, clien
protoReq v3lockpb.LockRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Lock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lock_Lock_0(ctx context.Context, marshaler runtime.Marshaler, server v3lockpb.LockServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -55,11 +56,11 @@ func local_request_Lock_Lock_0(ctx context.Context, marshaler runtime.Marshaler,
protoReq v3lockpb.LockRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Lock(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func request_Lock_Unlock_0(ctx context.Context, marshaler runtime.Marshaler, client v3lockpb.LockClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -67,11 +68,14 @@ func request_Lock_Unlock_0(ctx context.Context, marshaler runtime.Marshaler, cli
protoReq v3lockpb.UnlockRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
+ if req.Body != nil {
+ _, _ = io.Copy(io.Discard, req.Body)
+ }
msg, err := client.Unlock(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
func local_request_Lock_Unlock_0(ctx context.Context, marshaler runtime.Marshaler, server v3lockpb.LockServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@@ -79,11 +83,11 @@ func local_request_Lock_Unlock_0(ctx context.Context, marshaler runtime.Marshale
protoReq v3lockpb.UnlockRequest
metadata runtime.ServerMetadata
)
- if err := marshaler.NewDecoder(req.Body).Decode(protov1.MessageV2(&protoReq)); err != nil && !errors.Is(err, io.EOF) {
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.Unlock(ctx, &protoReq)
- return protov1.MessageV2(msg), metadata, err
+ return msg, metadata, err
}
// v3lockpb.RegisterLockHandlerServer registers the http handlers for service Lock to "mux".
diff --git a/server/etcdserver/api/v3lock/v3lockpb/v3lock.pb.go b/server/etcdserver/api/v3lock/v3lockpb/v3lock.pb.go
index 39c04abe179b..016b5965995a 100644
--- a/server/etcdserver/api/v3lock/v3lockpb/v3lock.pb.go
+++ b/server/etcdserver/api/v3lock/v3lockpb/v3lock.pb.go
@@ -1,36 +1,31 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: v3lock.proto
package v3lockpb
import (
- context "context"
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
etcdserverpb "go.etcd.io/etcd/api/v3/etcdserverpb"
_ "google.golang.org/genproto/googleapis/api/annotations"
- grpc "google.golang.org/grpc"
- codes "google.golang.org/grpc/codes"
- status "google.golang.org/grpc/status"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type LockRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// name is the identifier for the distributed shared lock to be acquired.
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// lease is the ID of the lease that will be attached to ownership of the
@@ -38,1106 +33,272 @@ type LockRequest struct {
// the lock is automatically released. Calls to Lock with the same lease will
// be treated as a single acquisition; locking twice with the same lease is a
// no-op.
- Lease int64 `protobuf:"varint,2,opt,name=lease,proto3" json:"lease,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Lease int64 `protobuf:"varint,2,opt,name=lease,proto3" json:"lease,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LockRequest) Reset() { *m = LockRequest{} }
-func (m *LockRequest) String() string { return proto.CompactTextString(m) }
-func (*LockRequest) ProtoMessage() {}
-func (*LockRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_52389b3e2f253201, []int{0}
+func (x *LockRequest) Reset() {
+ *x = LockRequest{}
+ mi := &file_v3lock_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LockRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
+
+func (x *LockRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LockRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+
+func (*LockRequest) ProtoMessage() {}
+
+func (x *LockRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_v3lock_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
-}
-func (m *LockRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LockRequest.Merge(m, src)
-}
-func (m *LockRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *LockRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LockRequest.DiscardUnknown(m)
+ return mi.MessageOf(x)
}
-var xxx_messageInfo_LockRequest proto.InternalMessageInfo
+// Deprecated: Use LockRequest.ProtoReflect.Descriptor instead.
+func (*LockRequest) Descriptor() ([]byte, []int) {
+ return file_v3lock_proto_rawDescGZIP(), []int{0}
+}
-func (m *LockRequest) GetName() []byte {
- if m != nil {
- return m.Name
+func (x *LockRequest) GetName() []byte {
+ if x != nil {
+ return x.Name
}
return nil
}
-func (m *LockRequest) GetLease() int64 {
- if m != nil {
- return m.Lease
+func (x *LockRequest) GetLease() int64 {
+ if x != nil {
+ return x.Lease
}
return 0
}
type LockResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
Header *etcdserverpb.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
// key is a key that will exist on etcd for the duration that the Lock caller
// owns the lock. Users should not modify this key or the lock may exhibit
// undefined behavior.
- Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LockResponse) Reset() { *m = LockResponse{} }
-func (m *LockResponse) String() string { return proto.CompactTextString(m) }
-func (*LockResponse) ProtoMessage() {}
-func (*LockResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_52389b3e2f253201, []int{1}
+func (x *LockResponse) Reset() {
+ *x = LockResponse{}
+ mi := &file_v3lock_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LockResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LockResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LockResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LockResponse.Merge(m, src)
-}
-func (m *LockResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *LockResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LockResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_LockResponse proto.InternalMessageInfo
-func (m *LockResponse) GetHeader() *etcdserverpb.ResponseHeader {
- if m != nil {
- return m.Header
- }
- return nil
+func (x *LockResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LockResponse) GetKey() []byte {
- if m != nil {
- return m.Key
- }
- return nil
-}
-
-type UnlockRequest struct {
- // key is the lock ownership key granted by Lock.
- Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
+func (*LockResponse) ProtoMessage() {}
-func (m *UnlockRequest) Reset() { *m = UnlockRequest{} }
-func (m *UnlockRequest) String() string { return proto.CompactTextString(m) }
-func (*UnlockRequest) ProtoMessage() {}
-func (*UnlockRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_52389b3e2f253201, []int{2}
-}
-func (m *UnlockRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *UnlockRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_UnlockRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *LockResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_v3lock_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
- }
-}
-func (m *UnlockRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_UnlockRequest.Merge(m, src)
-}
-func (m *UnlockRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *UnlockRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_UnlockRequest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_UnlockRequest proto.InternalMessageInfo
-
-func (m *UnlockRequest) GetKey() []byte {
- if m != nil {
- return m.Key
+ return ms
}
- return nil
+ return mi.MessageOf(x)
}
-type UnlockResponse struct {
- Header *etcdserverpb.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *UnlockResponse) Reset() { *m = UnlockResponse{} }
-func (m *UnlockResponse) String() string { return proto.CompactTextString(m) }
-func (*UnlockResponse) ProtoMessage() {}
-func (*UnlockResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_52389b3e2f253201, []int{3}
-}
-func (m *UnlockResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *UnlockResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_UnlockResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *UnlockResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_UnlockResponse.Merge(m, src)
-}
-func (m *UnlockResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *UnlockResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_UnlockResponse.DiscardUnknown(m)
+// Deprecated: Use LockResponse.ProtoReflect.Descriptor instead.
+func (*LockResponse) Descriptor() ([]byte, []int) {
+ return file_v3lock_proto_rawDescGZIP(), []int{1}
}
-var xxx_messageInfo_UnlockResponse proto.InternalMessageInfo
-
-func (m *UnlockResponse) GetHeader() *etcdserverpb.ResponseHeader {
- if m != nil {
- return m.Header
+func (x *LockResponse) GetHeader() *etcdserverpb.ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func init() {
- proto.RegisterType((*LockRequest)(nil), "v3lockpb.LockRequest")
- proto.RegisterType((*LockResponse)(nil), "v3lockpb.LockResponse")
- proto.RegisterType((*UnlockRequest)(nil), "v3lockpb.UnlockRequest")
- proto.RegisterType((*UnlockResponse)(nil), "v3lockpb.UnlockResponse")
-}
-
-func init() { proto.RegisterFile("v3lock.proto", fileDescriptor_52389b3e2f253201) }
-
-var fileDescriptor_52389b3e2f253201 = []byte{
- // 356 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xcd, 0x4a, 0xc3, 0x40,
- 0x10, 0xc7, 0xdd, 0xb6, 0x16, 0xd9, 0xa6, 0x2a, 0x4b, 0xd5, 0x10, 0x4a, 0xac, 0x39, 0x15, 0x0f,
- 0x59, 0x68, 0x05, 0xc5, 0xa3, 0x07, 0x11, 0x11, 0x84, 0x80, 0x0a, 0xde, 0xd2, 0x74, 0x88, 0xa5,
- 0x71, 0x27, 0x26, 0x69, 0xc1, 0xab, 0xaf, 0xe0, 0xc5, 0xc7, 0xf0, 0x31, 0x3c, 0x0a, 0xbe, 0x80,
- 0x54, 0x1f, 0x44, 0x32, 0x9b, 0xd8, 0xaa, 0x47, 0x2f, 0xc9, 0xec, 0xce, 0x6f, 0xfe, 0xf3, 0xb1,
- 0xc3, 0x8d, 0x69, 0x3f, 0xc2, 0x60, 0xec, 0xc6, 0x09, 0x66, 0x28, 0x56, 0xf4, 0x29, 0x1e, 0x58,
- 0xad, 0x10, 0x43, 0xa4, 0x4b, 0x99, 0x5b, 0xda, 0x6f, 0x6d, 0x43, 0x16, 0x0c, 0xa5, 0x1f, 0x8f,
- 0x64, 0x6e, 0xa4, 0x90, 0x4c, 0x21, 0x89, 0x07, 0x32, 0x89, 0x83, 0x02, 0x68, 0x87, 0x88, 0x61,
- 0x04, 0x84, 0xf8, 0x4a, 0x61, 0xe6, 0x67, 0x23, 0x54, 0xa9, 0xf6, 0x3a, 0xfb, 0xbc, 0x71, 0x86,
- 0xc1, 0xd8, 0x83, 0xbb, 0x09, 0xa4, 0x99, 0x10, 0xbc, 0xa6, 0xfc, 0x5b, 0x30, 0x59, 0x87, 0x75,
- 0x0d, 0x8f, 0x6c, 0xd1, 0xe2, 0xcb, 0x11, 0xf8, 0x29, 0x98, 0x95, 0x0e, 0xeb, 0x56, 0x3d, 0x7d,
- 0x70, 0x2e, 0xb9, 0xa1, 0x03, 0xd3, 0x18, 0x55, 0x0a, 0x62, 0x8f, 0xd7, 0x6f, 0xc0, 0x1f, 0x42,
- 0x42, 0xb1, 0x8d, 0x5e, 0xdb, 0x5d, 0xac, 0xc7, 0x2d, 0xb9, 0x13, 0x62, 0xbc, 0x82, 0x15, 0xeb,
- 0xbc, 0x3a, 0x86, 0x7b, 0x52, 0x36, 0xbc, 0xdc, 0x74, 0x76, 0x78, 0xf3, 0x42, 0x45, 0x0b, 0x25,
- 0x15, 0x08, 0x9b, 0x23, 0xc7, 0x7c, 0xb5, 0x44, 0xfe, 0x93, 0xbc, 0xf7, 0xcc, 0x78, 0x2d, 0xef,
- 0x41, 0x9c, 0x17, 0xff, 0x0d, 0xb7, 0x1c, 0xb6, 0xbb, 0x30, 0x14, 0x6b, 0xf3, 0xf7, 0xb5, 0x56,
- 0x73, 0xcc, 0x87, 0xb7, 0xcf, 0xc7, 0x8a, 0x70, 0x9a, 0x72, 0xda, 0x97, 0x39, 0x40, 0x9f, 0x43,
- 0xb6, 0x2b, 0xae, 0x78, 0x5d, 0x57, 0x28, 0xb6, 0xe6, 0xb1, 0x3f, 0xda, 0xb2, 0xcc, 0xbf, 0x8e,
- 0x42, 0xd6, 0x22, 0xd9, 0x96, 0xb3, 0xf6, 0x2d, 0x3b, 0x51, 0x85, 0xf0, 0xd1, 0xe9, 0xcb, 0xcc,
- 0x66, 0xaf, 0x33, 0x9b, 0xbd, 0xcf, 0x6c, 0xf6, 0xf4, 0x61, 0x2f, 0x5d, 0x1f, 0x84, 0x48, 0xcd,
- 0xba, 0x23, 0xa4, 0x0d, 0x90, 0xba, 0xeb, 0x3c, 0x76, 0x3e, 0x03, 0x7a, 0x7c, 0x9d, 0x4f, 0x96,
- 0x69, 0x07, 0x75, 0xda, 0x80, 0xfe, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x48, 0x31, 0x4a,
- 0x70, 0x02, 0x00, 0x00,
-}
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion4
-
-// LockClient is the client API for Lock service.
-//
-// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
-type LockClient interface {
- // Lock acquires a distributed shared lock on a given named lock.
- // On success, it will return a unique key that exists so long as the
- // lock is held by the caller. This key can be used in conjunction with
- // transactions to safely ensure updates to etcd only occur while holding
- // lock ownership. The lock is held until Unlock is called on the key or the
- // lease associate with the owner expires.
- Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error)
- // Unlock takes a key returned by Lock and releases the hold on lock. The
- // next Lock caller waiting for the lock will then be woken up and given
- // ownership of the lock.
- Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error)
-}
-
-type lockClient struct {
- cc *grpc.ClientConn
-}
-
-func NewLockClient(cc *grpc.ClientConn) LockClient {
- return &lockClient{cc}
-}
-
-func (c *lockClient) Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error) {
- out := new(LockResponse)
- err := c.cc.Invoke(ctx, "/v3lockpb.Lock/Lock", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-func (c *lockClient) Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error) {
- out := new(UnlockResponse)
- err := c.cc.Invoke(ctx, "/v3lockpb.Lock/Unlock", in, out, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// LockServer is the server API for Lock service.
-type LockServer interface {
- // Lock acquires a distributed shared lock on a given named lock.
- // On success, it will return a unique key that exists so long as the
- // lock is held by the caller. This key can be used in conjunction with
- // transactions to safely ensure updates to etcd only occur while holding
- // lock ownership. The lock is held until Unlock is called on the key or the
- // lease associate with the owner expires.
- Lock(context.Context, *LockRequest) (*LockResponse, error)
- // Unlock takes a key returned by Lock and releases the hold on lock. The
- // next Lock caller waiting for the lock will then be woken up and given
- // ownership of the lock.
- Unlock(context.Context, *UnlockRequest) (*UnlockResponse, error)
-}
-
-// UnimplementedLockServer can be embedded to have forward compatible implementations.
-type UnimplementedLockServer struct {
-}
-
-func (*UnimplementedLockServer) Lock(ctx context.Context, req *LockRequest) (*LockResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Lock not implemented")
-}
-func (*UnimplementedLockServer) Unlock(ctx context.Context, req *UnlockRequest) (*UnlockResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method Unlock not implemented")
-}
-
-func RegisterLockServer(s *grpc.Server, srv LockServer) {
- s.RegisterService(&_Lock_serviceDesc, srv)
-}
-
-func _Lock_Lock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(LockRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(LockServer).Lock(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/v3lockpb.Lock/Lock",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LockServer).Lock(ctx, req.(*LockRequest))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-func _Lock_Unlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(UnlockRequest)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(LockServer).Unlock(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/v3lockpb.Lock/Unlock",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(LockServer).Unlock(ctx, req.(*UnlockRequest))
+func (x *LockResponse) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
- return interceptor(ctx, in, info, handler)
+ return nil
}
-var _Lock_serviceDesc = grpc.ServiceDesc{
- ServiceName: "v3lockpb.Lock",
- HandlerType: (*LockServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "Lock",
- Handler: _Lock_Lock_Handler,
- },
- {
- MethodName: "Unlock",
- Handler: _Lock_Unlock_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "v3lock.proto",
+type UnlockRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // key is the lock ownership key granted by Lock.
+ Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LockRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *UnlockRequest) Reset() {
+ *x = UnlockRequest{}
+ mi := &file_v3lock_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LockRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *UnlockRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Lease != 0 {
- i = encodeVarintV3Lock(dAtA, i, uint64(m.Lease))
- i--
- dAtA[i] = 0x10
- }
- if len(m.Name) > 0 {
- i -= len(m.Name)
- copy(dAtA[i:], m.Name)
- i = encodeVarintV3Lock(dAtA, i, uint64(len(m.Name)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
-}
+func (*UnlockRequest) ProtoMessage() {}
-func (m *LockResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *LockResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *LockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintV3Lock(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0x12
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Lock(dAtA, i, uint64(size))
+func (x *UnlockRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_v3lock_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0xa
+ return ms
}
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func (m *UnlockRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
-}
-
-func (m *UnlockRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *UnlockRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if len(m.Key) > 0 {
- i -= len(m.Key)
- copy(dAtA[i:], m.Key)
- i = encodeVarintV3Lock(dAtA, i, uint64(len(m.Key)))
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+// Deprecated: Use UnlockRequest.ProtoReflect.Descriptor instead.
+func (*UnlockRequest) Descriptor() ([]byte, []int) {
+ return file_v3lock_proto_rawDescGZIP(), []int{2}
}
-func (m *UnlockResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *UnlockRequest) GetKey() []byte {
+ if x != nil {
+ return x.Key
}
- return dAtA[:n], nil
+ return nil
}
-func (m *UnlockResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type UnlockResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Header *etcdserverpb.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *UnlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Header != nil {
- {
- size, err := m.Header.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintV3Lock(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *UnlockResponse) Reset() {
+ *x = UnlockResponse{}
+ mi := &file_v3lock_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func encodeVarintV3Lock(dAtA []byte, offset int, v uint64) int {
- offset -= sovV3Lock(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *LockRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Name)
- if l > 0 {
- n += 1 + l + sovV3Lock(uint64(l))
- }
- if m.Lease != 0 {
- n += 1 + sovV3Lock(uint64(m.Lease))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+func (x *UnlockResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LockResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Lock(uint64(l))
- }
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovV3Lock(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
+func (*UnlockResponse) ProtoMessage() {}
-func (m *UnlockRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- l = len(m.Key)
- if l > 0 {
- n += 1 + l + sovV3Lock(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func (m *UnlockResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.Header != nil {
- l = m.Header.Size()
- n += 1 + l + sovV3Lock(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
-}
-
-func sovV3Lock(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozV3Lock(x uint64) (n int) {
- return sovV3Lock(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *LockRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LockRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LockRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Lock
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Lock
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Name = append(m.Name[:0], dAtA[iNdEx:postIndex]...)
- if m.Name == nil {
- m.Name = []byte{}
- }
- iNdEx = postIndex
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
- }
- m.Lease = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Lease |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipV3Lock(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Lock
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
+func (x *UnlockResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_v3lock_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+ return mi.MessageOf(x)
}
-func (m *LockResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LockResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LockResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Lock
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Lock
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- case 2:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Lock
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Lock
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Lock(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Lock
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+// Deprecated: Use UnlockResponse.ProtoReflect.Descriptor instead.
+func (*UnlockResponse) Descriptor() ([]byte, []int) {
+ return file_v3lock_proto_rawDescGZIP(), []int{3}
}
-func (m *UnlockRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: UnlockRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: UnlockRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthV3Lock
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthV3Lock
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
- if m.Key == nil {
- m.Key = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Lock(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Lock
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *UnlockResponse) GetHeader() *etcdserverpb.ResponseHeader {
+ if x != nil {
+ return x.Header
}
return nil
}
-func (m *UnlockResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: UnlockResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: UnlockResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthV3Lock
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthV3Lock
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.Header == nil {
- m.Header = &etcdserverpb.ResponseHeader{}
- }
- if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipV3Lock(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthV3Lock
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
-}
-func skipV3Lock(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowV3Lock
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthV3Lock
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupV3Lock
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthV3Lock
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+var File_v3lock_proto protoreflect.FileDescriptor
+
+const file_v3lock_proto_rawDesc = "" +
+ "\n" +
+ "\fv3lock.proto\x12\bv3lockpb\x1a\x1fetcd/api/etcdserverpb/rpc.proto\x1a\x1cgoogle/api/annotations.proto\"7\n" +
+ "\vLockRequest\x12\x12\n" +
+ "\x04name\x18\x01 \x01(\fR\x04name\x12\x14\n" +
+ "\x05lease\x18\x02 \x01(\x03R\x05lease\"V\n" +
+ "\fLockResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header\x12\x10\n" +
+ "\x03key\x18\x02 \x01(\fR\x03key\"!\n" +
+ "\rUnlockRequest\x12\x10\n" +
+ "\x03key\x18\x01 \x01(\fR\x03key\"F\n" +
+ "\x0eUnlockResponse\x124\n" +
+ "\x06header\x18\x01 \x01(\v2\x1c.etcdserverpb.ResponseHeaderR\x06header2\xb0\x01\n" +
+ "\x04Lock\x12O\n" +
+ "\x04Lock\x12\x15.v3lockpb.LockRequest\x1a\x16.v3lockpb.LockResponse\"\x18\x82\xd3\xe4\x93\x02\x12:\x01*\"\r/v3/lock/lock\x12W\n" +
+ "\x06Unlock\x12\x17.v3lockpb.UnlockRequest\x1a\x18.v3lockpb.UnlockResponse\"\x1a\x82\xd3\xe4\x93\x02\x14:\x01*\"\x0f/v3/lock/unlockB:Z8go.etcd.io/etcd/server/v3/etcdserver/api/v3lock/v3lockpbb\x06proto3"
var (
- ErrInvalidLengthV3Lock = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowV3Lock = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupV3Lock = fmt.Errorf("proto: unexpected end of group")
+ file_v3lock_proto_rawDescOnce sync.Once
+ file_v3lock_proto_rawDescData []byte
)
+
+func file_v3lock_proto_rawDescGZIP() []byte {
+ file_v3lock_proto_rawDescOnce.Do(func() {
+ file_v3lock_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_v3lock_proto_rawDesc), len(file_v3lock_proto_rawDesc)))
+ })
+ return file_v3lock_proto_rawDescData
+}
+
+var file_v3lock_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_v3lock_proto_goTypes = []any{
+ (*LockRequest)(nil), // 0: v3lockpb.LockRequest
+ (*LockResponse)(nil), // 1: v3lockpb.LockResponse
+ (*UnlockRequest)(nil), // 2: v3lockpb.UnlockRequest
+ (*UnlockResponse)(nil), // 3: v3lockpb.UnlockResponse
+ (*etcdserverpb.ResponseHeader)(nil), // 4: etcdserverpb.ResponseHeader
+}
+var file_v3lock_proto_depIdxs = []int32{
+ 4, // 0: v3lockpb.LockResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 4, // 1: v3lockpb.UnlockResponse.header:type_name -> etcdserverpb.ResponseHeader
+ 0, // 2: v3lockpb.Lock.Lock:input_type -> v3lockpb.LockRequest
+ 2, // 3: v3lockpb.Lock.Unlock:input_type -> v3lockpb.UnlockRequest
+ 1, // 4: v3lockpb.Lock.Lock:output_type -> v3lockpb.LockResponse
+ 3, // 5: v3lockpb.Lock.Unlock:output_type -> v3lockpb.UnlockResponse
+ 4, // [4:6] is the sub-list for method output_type
+ 2, // [2:4] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_v3lock_proto_init() }
+func file_v3lock_proto_init() {
+ if File_v3lock_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_v3lock_proto_rawDesc), len(file_v3lock_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 4,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_v3lock_proto_goTypes,
+ DependencyIndexes: file_v3lock_proto_depIdxs,
+ MessageInfos: file_v3lock_proto_msgTypes,
+ }.Build()
+ File_v3lock_proto = out.File
+ file_v3lock_proto_goTypes = nil
+ file_v3lock_proto_depIdxs = nil
+}
diff --git a/server/etcdserver/api/v3lock/v3lockpb/v3lock.proto b/server/etcdserver/api/v3lock/v3lockpb/v3lock.proto
index 88a1c8242849..bcc074d1b264 100644
--- a/server/etcdserver/api/v3lock/v3lockpb/v3lock.proto
+++ b/server/etcdserver/api/v3lock/v3lockpb/v3lock.proto
@@ -1,7 +1,6 @@
syntax = "proto3";
package v3lockpb;
-import "gogoproto/gogo.proto";
import "etcd/api/etcdserverpb/rpc.proto";
// for grpc-gateway
@@ -9,9 +8,6 @@ import "google/api/annotations.proto";
option go_package = "go.etcd.io/etcd/server/v3/etcdserver/api/v3lock/v3lockpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-
// The lock service exposes client-side locking facilities as a gRPC interface.
service Lock {
// Lock acquires a distributed shared lock on a given named lock.
diff --git a/server/etcdserver/api/v3lock/v3lockpb/v3lock_grpc.pb.go b/server/etcdserver/api/v3lock/v3lockpb/v3lock_grpc.pb.go
new file mode 100644
index 000000000000..466d8e0de73a
--- /dev/null
+++ b/server/etcdserver/api/v3lock/v3lockpb/v3lock_grpc.pb.go
@@ -0,0 +1,182 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.6.2
+// - protoc v3.20.3
+// source: v3lock.proto
+
+package v3lockpb
+
+import (
+ context "context"
+
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ Lock_Lock_FullMethodName = "/v3lockpb.Lock/Lock"
+ Lock_Unlock_FullMethodName = "/v3lockpb.Lock/Unlock"
+)
+
+// LockClient is the client API for Lock service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// The lock service exposes client-side locking facilities as a gRPC interface.
+type LockClient interface {
+ // Lock acquires a distributed shared lock on a given named lock.
+ // On success, it will return a unique key that exists so long as the
+ // lock is held by the caller. This key can be used in conjunction with
+ // transactions to safely ensure updates to etcd only occur while holding
+ // lock ownership. The lock is held until Unlock is called on the key or the
+ // lease associate with the owner expires.
+ Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error)
+ // Unlock takes a key returned by Lock and releases the hold on lock. The
+ // next Lock caller waiting for the lock will then be woken up and given
+ // ownership of the lock.
+ Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error)
+}
+
+type lockClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewLockClient(cc grpc.ClientConnInterface) LockClient {
+ return &lockClient{cc}
+}
+
+func (c *lockClient) Lock(ctx context.Context, in *LockRequest, opts ...grpc.CallOption) (*LockResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(LockResponse)
+ err := c.cc.Invoke(ctx, Lock_Lock_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *lockClient) Unlock(ctx context.Context, in *UnlockRequest, opts ...grpc.CallOption) (*UnlockResponse, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(UnlockResponse)
+ err := c.cc.Invoke(ctx, Lock_Unlock_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// LockServer is the server API for Lock service.
+// All implementations must embed UnimplementedLockServer
+// for forward compatibility.
+//
+// The lock service exposes client-side locking facilities as a gRPC interface.
+type LockServer interface {
+ // Lock acquires a distributed shared lock on a given named lock.
+ // On success, it will return a unique key that exists so long as the
+ // lock is held by the caller. This key can be used in conjunction with
+ // transactions to safely ensure updates to etcd only occur while holding
+ // lock ownership. The lock is held until Unlock is called on the key or the
+ // lease associate with the owner expires.
+ Lock(context.Context, *LockRequest) (*LockResponse, error)
+ // Unlock takes a key returned by Lock and releases the hold on lock. The
+ // next Lock caller waiting for the lock will then be woken up and given
+ // ownership of the lock.
+ Unlock(context.Context, *UnlockRequest) (*UnlockResponse, error)
+ mustEmbedUnimplementedLockServer()
+}
+
+// UnimplementedLockServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedLockServer struct{}
+
+func (UnimplementedLockServer) Lock(context.Context, *LockRequest) (*LockResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Lock not implemented")
+}
+func (UnimplementedLockServer) Unlock(context.Context, *UnlockRequest) (*UnlockResponse, error) {
+ return nil, status.Error(codes.Unimplemented, "method Unlock not implemented")
+}
+func (UnimplementedLockServer) mustEmbedUnimplementedLockServer() {}
+func (UnimplementedLockServer) testEmbeddedByValue() {}
+
+// UnsafeLockServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to LockServer will
+// result in compilation errors.
+type UnsafeLockServer interface {
+ mustEmbedUnimplementedLockServer()
+}
+
+func RegisterLockServer(s grpc.ServiceRegistrar, srv LockServer) {
+ // If the following call panics, it indicates UnimplementedLockServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
+ s.RegisterService(&Lock_ServiceDesc, srv)
+}
+
+func _Lock_Lock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(LockRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LockServer).Lock(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lock_Lock_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LockServer).Lock(ctx, req.(*LockRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Lock_Unlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(UnlockRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(LockServer).Unlock(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Lock_Unlock_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(LockServer).Unlock(ctx, req.(*UnlockRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// Lock_ServiceDesc is the grpc.ServiceDesc for Lock service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Lock_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "v3lockpb.Lock",
+ HandlerType: (*LockServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Lock",
+ Handler: _Lock_Lock_Handler,
+ },
+ {
+ MethodName: "Unlock",
+ Handler: _Lock_Unlock_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "v3lock.proto",
+}
diff --git a/server/etcdserver/api/v3rpc/auth.go b/server/etcdserver/api/v3rpc/auth.go
index 15ef5f34777f..15b0bdd13619 100644
--- a/server/etcdserver/api/v3rpc/auth.go
+++ b/server/etcdserver/api/v3rpc/auth.go
@@ -24,6 +24,8 @@ import (
type AuthServer struct {
authenticator etcdserver.Authenticator
+ // we want compile errors if new methods are added
+ pb.UnsafeAuthServer
}
func NewAuthServer(s *etcdserver.EtcdServer) *AuthServer {
diff --git a/server/etcdserver/api/v3rpc/codec.go b/server/etcdserver/api/v3rpc/codec.go
index 1bbed8397124..3264b2e7ef0f 100644
--- a/server/etcdserver/api/v3rpc/codec.go
+++ b/server/etcdserver/api/v3rpc/codec.go
@@ -14,7 +14,7 @@
package v3rpc
-import "github.com/golang/protobuf/proto"
+import "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
type codec struct{}
diff --git a/server/etcdserver/api/v3rpc/grpc.go b/server/etcdserver/api/v3rpc/grpc.go
index 57ed6eabb812..91863e294985 100644
--- a/server/etcdserver/api/v3rpc/grpc.go
+++ b/server/etcdserver/api/v3rpc/grpc.go
@@ -43,7 +43,7 @@ var (
func Server(s *etcdserver.EtcdServer, tls *tls.Config, interceptor grpc.UnaryServerInterceptor, gopts ...grpc.ServerOption) *grpc.Server {
var opts []grpc.ServerOption
- opts = append(opts, grpc.CustomCodec(&codec{}))
+ opts = append(opts, grpc.CustomCodec(&codec{})) //nolint:staticcheck // TODO: remove for a supported version
if tls != nil {
opts = append(opts, grpc.Creds(credentials.NewTransportCredential(tls)))
}
diff --git a/server/etcdserver/api/v3rpc/header.go b/server/etcdserver/api/v3rpc/header.go
index 8fe4e5863b8b..8986a5985050 100644
--- a/server/etcdserver/api/v3rpc/header.go
+++ b/server/etcdserver/api/v3rpc/header.go
@@ -38,13 +38,20 @@ func newHeader(s *etcdserver.EtcdServer) header {
// fill populates pb.ResponseHeader using etcdserver information
func (h *header) fill(rh *pb.ResponseHeader) {
+ h.fillWithoutRevision(rh)
+ if rh.Revision == 0 {
+ rh.Revision = h.rev()
+ }
+}
+
+// fillWithoutRevision populates pb.ResponseHeader except for Revision.
+// Streaming handlers use this because the pinned read revision must be set
+// by the handler rather than silently replaced with the live store revision.
+func (h *header) fillWithoutRevision(rh *pb.ResponseHeader) {
if rh == nil {
panic("unexpected nil resp.Header")
}
rh.ClusterId = uint64(h.clusterID)
rh.MemberId = uint64(h.memberID)
rh.RaftTerm = h.sg.Term()
- if rh.Revision == 0 {
- rh.Revision = h.rev()
- }
}
diff --git a/server/etcdserver/api/v3rpc/interceptor.go b/server/etcdserver/api/v3rpc/interceptor.go
index 697d0b075ed9..f09ec425580a 100644
--- a/server/etcdserver/api/v3rpc/interceptor.go
+++ b/server/etcdserver/api/v3rpc/interceptor.go
@@ -24,6 +24,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
@@ -113,35 +114,35 @@ func logUnaryRequestStats(ctx context.Context, lg *zap.Logger, warnLatency time.
_req, ok := req.(*pb.RangeRequest)
if ok {
reqCount = 0
- reqSize = _req.Size()
+ reqSize = proto.Size(_req)
reqContent = _req.String()
}
if _resp != nil {
respCount = _resp.GetCount()
- respSize = _resp.Size()
+ respSize = proto.Size(_resp)
}
case *pb.PutResponse:
_req, ok := req.(*pb.PutRequest)
if ok {
reqCount = 1
- reqSize = _req.Size()
+ reqSize = proto.Size(_req)
reqContent = pb.NewLoggablePutRequest(_req).String()
// redact value field from request content, see PR #9821
}
if _resp != nil {
respCount = 0
- respSize = _resp.Size()
+ respSize = proto.Size(_resp)
}
case *pb.DeleteRangeResponse:
_req, ok := req.(*pb.DeleteRangeRequest)
if ok {
reqCount = 0
- reqSize = _req.Size()
+ reqSize = proto.Size(_req)
reqContent = _req.String()
}
if _resp != nil {
respCount = _resp.GetDeleted()
- respSize = _resp.Size()
+ respSize = proto.Size(_resp)
}
case *pb.TxnResponse:
_req, ok := req.(*pb.TxnRequest)
@@ -150,13 +151,13 @@ func logUnaryRequestStats(ctx context.Context, lg *zap.Logger, warnLatency time.
reqCount = int64(len(_req.GetSuccess()))
reqSize = 0
for _, r := range _req.GetSuccess() {
- reqSize += r.Size()
+ reqSize += proto.Size(r)
}
} else {
reqCount = int64(len(_req.GetFailure()))
reqSize = 0
for _, r := range _req.GetFailure() {
- reqSize += r.Size()
+ reqSize += proto.Size(r)
}
}
reqContent = pb.NewLoggableTxnRequest(_req).String()
@@ -164,7 +165,7 @@ func logUnaryRequestStats(ctx context.Context, lg *zap.Logger, warnLatency time.
}
if _resp != nil {
respCount = 0
- respSize = _resp.Size()
+ respSize = proto.Size(_resp)
}
default:
reqCount = -1
diff --git a/server/etcdserver/api/v3rpc/key.go b/server/etcdserver/api/v3rpc/key.go
index 3da35922c2c2..4f87716a73aa 100644
--- a/server/etcdserver/api/v3rpc/key.go
+++ b/server/etcdserver/api/v3rpc/key.go
@@ -18,10 +18,14 @@ package v3rpc
import (
"context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/pkg/v3/adt"
"go.etcd.io/etcd/server/v3/etcdserver"
+ "go.etcd.io/etcd/server/v3/etcdserver/txn"
)
type kvServer struct {
@@ -33,6 +37,8 @@ type kvServer struct {
// Txn.Success can have at most 128 operations,
// and Txn.Failure can have at most 128 operations.
maxTxnOps uint
+ // we want compile errors if new methods are added
+ pb.UnsafeKVServer
}
func NewKVServer(s *etcdserver.EtcdServer) pb.KVServer {
@@ -53,6 +59,34 @@ func (s *kvServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResp
return resp, nil
}
+func (s *kvServer) RangeStream(r *pb.RangeRequest, rs pb.KV_RangeStreamServer) error {
+ if err := checkRangeStreamRequest(r); err != nil {
+ return err
+ }
+ err := s.kv.RangeStream(r, &headerFillingRangeStream{KV_RangeStreamServer: rs, hdr: &s.hdr})
+ if err != nil {
+ return togRPCError(err)
+ }
+ return nil
+}
+
+// headerFillingRangeStream wraps KV_RangeStreamServer to fill the cluster
+// header (cluster ID, member ID, raft term) on the chunk that carries it.
+// Revision is not filled: the handler must set it to the pinned read
+// revision so that a missing value surfaces as zero instead of being
+// silently replaced with the live store revision.
+type headerFillingRangeStream struct {
+ pb.KV_RangeStreamServer
+ hdr *header
+}
+
+func (s *headerFillingRangeStream) Send(resp *pb.RangeStreamResponse) error {
+ if resp.RangeResponse.Header != nil {
+ s.hdr.fillWithoutRevision(resp.RangeResponse.Header)
+ }
+ return s.KV_RangeStreamServer.Send(resp)
+}
+
func (s *kvServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
if err := checkPutRequest(r); err != nil {
return nil, err
@@ -132,6 +166,19 @@ func checkRangeRequest(r *pb.RangeRequest) error {
return nil
}
+func checkRangeStreamRequest(r *pb.RangeRequest) error {
+ if err := checkRangeRequest(r); err != nil {
+ return err
+ }
+ if !txn.IsDefaultOrdering(r.SortTarget, r.SortOrder) {
+ return status.Errorf(codes.Unimplemented, "RangeStream does not support custom sort orders")
+ }
+ if txn.HasRevisionFilters(r) {
+ return status.Errorf(codes.Unimplemented, "RangeStream does not support revision filters")
+ }
+ return nil
+}
+
func checkPutRequest(r *pb.PutRequest) error {
if len(r.Key) == 0 {
return rpctypes.ErrGRPCEmptyKey
diff --git a/server/etcdserver/api/v3rpc/lease.go b/server/etcdserver/api/v3rpc/lease.go
index f51334eadeb8..97ee8a901bd3 100644
--- a/server/etcdserver/api/v3rpc/lease.go
+++ b/server/etcdserver/api/v3rpc/lease.go
@@ -22,7 +22,6 @@ import (
"go.uber.org/zap"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/server/v3/lease"
)
@@ -31,6 +30,7 @@ type LeaseServer struct {
lg *zap.Logger
hdr header
le etcdserver.Lessor
+ pb.UnsafeLeaseServer
}
func NewLeaseServer(s *etcdserver.EtcdServer) pb.LeaseServer {
@@ -98,11 +98,12 @@ func (ls *LeaseServer) LeaseKeepAlive(stream pb.Lease_LeaseKeepAliveServer) (err
select {
case err = <-errc:
case <-stream.Context().Done():
- // the only server-side cancellation is noleader for now.
+ // We end up here due to:
+ // 1. Client cancellation
+ // 2. Server cancellation: the client ctx is wrapped with WithRequireLeader,
+ // monitorLeader() detects no leader and thus cancels this stream with ErrGRPCNoLeader.
+ // 3. Server cancellation: the server is shutting down.
err = stream.Context().Err()
- if errors.Is(err, context.Canceled) {
- err = rpctypes.ErrGRPCNoLeader
- }
}
return err
}
diff --git a/server/etcdserver/api/v3rpc/maintenance.go b/server/etcdserver/api/v3rpc/maintenance.go
index cf65dad0da7c..432b30d96e88 100644
--- a/server/etcdserver/api/v3rpc/maintenance.go
+++ b/server/etcdserver/api/v3rpc/maintenance.go
@@ -89,6 +89,9 @@ type maintenanceServer struct {
cg ConfigGetter
healthNotifier notifier
+
+ // we want compile errors if new methods are added
+ pb.UnsafeMaintenanceServer
}
func NewMaintenanceServer(s *etcdserver.EtcdServer, healthNotifier notifier) pb.MaintenanceServer {
@@ -364,7 +367,7 @@ func (ams *authMaintenanceServer) Alarm(ctx context.Context, ar *pb.AlarmRequest
}
func (ams *authMaintenanceServer) Status(ctx context.Context, ar *pb.StatusRequest) (*pb.StatusResponse, error) {
- if err := ams.isPermitted(ctx); err != nil {
+ if err := ams.requireAuthInfo(ctx); err != nil {
return nil, togRPCError(err)
}
diff --git a/server/etcdserver/api/v3rpc/member.go b/server/etcdserver/api/v3rpc/member.go
index 57768b39256a..29293e0f535a 100644
--- a/server/etcdserver/api/v3rpc/member.go
+++ b/server/etcdserver/api/v3rpc/member.go
@@ -29,6 +29,8 @@ import (
type ClusterServer struct {
cluster api.Cluster
server *etcdserver.EtcdServer
+ // we want compile errors if new methods are added
+ pb.UnsafeClusterServer
}
func NewClusterServer(s *etcdserver.EtcdServer) *ClusterServer {
diff --git a/server/etcdserver/api/v3rpc/metrics.go b/server/etcdserver/api/v3rpc/metrics.go
index d79506e07321..419b81173fb5 100644
--- a/server/etcdserver/api/v3rpc/metrics.go
+++ b/server/etcdserver/api/v3rpc/metrics.go
@@ -52,6 +52,54 @@ var (
},
[]string{"type", "client_api_version"},
)
+
+ watchSendLoopWatchStreamDuration = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd_debugging",
+ Subsystem: "server",
+ Name: "watch_send_loop_watch_stream_duration_seconds",
+ Help: "The total duration in seconds of running through the send loop watch stream response all events.",
+ // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2
+ // highest bucket start of 0.001 sec * 2^13 == 8.192 sec
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
+ },
+ )
+
+ watchSendLoopWatchStreamDurationPerEvent = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd_debugging",
+ Subsystem: "server",
+ Name: "watch_send_loop_watch_stream_duration_per_event_seconds",
+ Help: "The average duration in seconds of running through the send loop watch stream response, per event.",
+ // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2
+ // highest bucket start of 0.001 sec * 2^13 == 8.192 sec
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
+ },
+ )
+
+ watchSendLoopControlStreamDuration = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd_debugging",
+ Subsystem: "server",
+ Name: "watch_send_loop_control_stream_duration_seconds",
+ Help: "The total duration in seconds of running through the send loop control stream response.",
+ // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2
+ // highest bucket start of 0.001 sec * 2^13 == 8.192 sec
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
+ },
+ )
+
+ watchSendLoopProgressDuration = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd_debugging",
+ Subsystem: "server",
+ Name: "watch_send_loop_progress_duration_seconds",
+ Help: "The total duration in seconds of running through the progress loop control stream response.",
+ // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2
+ // highest bucket start of 0.001 sec * 2^13 == 8.192 sec
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
+ },
+ )
)
func init() {
@@ -59,4 +107,8 @@ func init() {
prometheus.MustRegister(receivedBytes)
prometheus.MustRegister(streamFailures)
prometheus.MustRegister(clientRequests)
+ prometheus.MustRegister(watchSendLoopWatchStreamDuration)
+ prometheus.MustRegister(watchSendLoopWatchStreamDurationPerEvent)
+ prometheus.MustRegister(watchSendLoopControlStreamDuration)
+ prometheus.MustRegister(watchSendLoopProgressDuration)
}
diff --git a/server/etcdserver/api/v3rpc/util.go b/server/etcdserver/api/v3rpc/util.go
index 2354b0cb2431..3caeec6edb45 100644
--- a/server/etcdserver/api/v3rpc/util.go
+++ b/server/etcdserver/api/v3rpc/util.go
@@ -51,6 +51,7 @@ var toGRPCErrorMap = map[error]error{
errors.ErrNoLeader: rpctypes.ErrGRPCNoLeader,
errors.ErrNotLeader: rpctypes.ErrGRPCNotLeader,
errors.ErrLeaderChanged: rpctypes.ErrGRPCLeaderChanged,
+ errors.ErrCanceled: rpctypes.ErrGRPCCanceled,
errors.ErrStopped: rpctypes.ErrGRPCStopped,
errors.ErrTimeout: rpctypes.ErrGRPCTimeout,
errors.ErrTimeoutDueToLeaderFail: rpctypes.ErrGRPCTimeoutDueToLeaderFail,
diff --git a/server/etcdserver/api/v3rpc/validationfuzz_test.go b/server/etcdserver/api/v3rpc/validationfuzz_test.go
index d921c9602b73..61b234980d8e 100644
--- a/server/etcdserver/api/v3rpc/validationfuzz_test.go
+++ b/server/etcdserver/api/v3rpc/validationfuzz_test.go
@@ -28,7 +28,7 @@ import (
)
func FuzzTxnRangeRequest(f *testing.F) {
- testcases := []pb.RangeRequest{
+ testcases := []*pb.RangeRequest{
{
Key: []byte{2},
RangeEnd: []byte{2},
@@ -74,7 +74,7 @@ func FuzzTxnRangeRequest(f *testing.F) {
}
func FuzzTxnPutRequest(f *testing.F) {
- testcases := []pb.PutRequest{
+ testcases := []*pb.PutRequest{
{
Key: []byte{2},
Value: []byte{2},
@@ -119,7 +119,7 @@ func FuzzTxnPutRequest(f *testing.F) {
}
func FuzzTxnDeleteRangeRequest(f *testing.F) {
- testcases := []pb.DeleteRangeRequest{
+ testcases := []*pb.DeleteRangeRequest{
{
Key: []byte{2},
RangeEnd: []byte{2},
@@ -168,14 +168,14 @@ func execTransaction(t *testing.T, req *pb.RequestOp) {
defer s.Close()
// setup cancelled context
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
request := &pb.TxnRequest{
Success: []*pb.RequestOp{req},
}
- _, _, err := txn.Txn(ctx, zaptest.NewLogger(t), request, false, s, &lease.FakeLessor{})
+ _, _, err := txn.Txn(ctx, zaptest.NewLogger(t), request, false, s, &lease.FakeLessor{}, false)
if err != nil {
t.Skipf("Application erroring. %s", err.Error())
}
diff --git a/server/etcdserver/api/v3rpc/watch.go b/server/etcdserver/api/v3rpc/watch.go
index 02aa7c1bf29a..1fe1cbe13c33 100644
--- a/server/etcdserver/api/v3rpc/watch.go
+++ b/server/etcdserver/api/v3rpc/watch.go
@@ -22,13 +22,17 @@ import (
"sync"
"time"
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/client/pkg/v3/verify"
clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
"go.etcd.io/etcd/server/v3/auth"
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/server/v3/etcdserver/apply"
@@ -48,6 +52,9 @@ type watchServer struct {
sg apply.RaftStatusGetter
watchable mvcc.WatchableKV
ag AuthGetter
+
+ // we want compile errors if new methods are added
+ pb.UnsafeWatchServer
}
// NewWatchServer returns a new watch server.
@@ -320,8 +327,16 @@ func (sws *serverWatchStream) recvLoop() error {
}
filters := FiltersFromRequest(creq)
-
- id, err := sws.watchStream.Watch(mvcc.WatchID(creq.WatchId), creq.Key, creq.RangeEnd, creq.StartRevision, filters...)
+ ctx, _ := traceutil.Tracer.Start(sws.gRPCStream.Context(), "watch", trace.WithAttributes(
+ attribute.String("key", string(creq.Key)),
+ attribute.String("range_end", string(creq.RangeEnd)),
+ attribute.Int64("start_rev", creq.StartRevision),
+ attribute.Bool("progress_notify", creq.ProgressNotify),
+ attribute.Bool("prev_kv", creq.PrevKv),
+ attribute.Bool("fragment", creq.Fragment),
+ ))
+
+ id, err := sws.watchStream.Watch(ctx, mvcc.WatchID(creq.WatchId), creq.Key, creq.RangeEnd, creq.StartRevision, filters...)
if err == nil {
sws.mu.Lock()
if creq.ProgressNotify {
@@ -421,21 +436,21 @@ func (sws *serverWatchStream) sendLoop() {
return
}
- // TODO: evs is []mvccpb.Event type
- // either return []*mvccpb.Event from the mvcc package
- // or define protocol buffer with []mvccpb.Event.
+ start := time.Now()
+
+ // TODO(fuweid): do we still need copy here?
evs := wresp.Events
events := make([]*mvccpb.Event, len(evs))
sws.mu.RLock()
needPrevKV := sws.prevKV[wresp.WatchID]
sws.mu.RUnlock()
for i := range evs {
- events[i] = &evs[i]
+ events[i] = evs[i]
if needPrevKV && !IsCreateEvent(evs[i]) {
opt := mvcc.RangeOptions{Rev: evs[i].Kv.ModRevision - 1}
r, err := sws.watchable.Range(context.TODO(), evs[i].Kv.Key, nil, opt)
if err == nil && len(r.KVs) != 0 {
- events[i].PrevKv = &(r.KVs[0])
+ events[i].PrevKv = r.KVs[0]
}
}
}
@@ -491,11 +506,15 @@ func (sws *serverWatchStream) sendLoop() {
}
sws.mu.Unlock()
+ totalDur := time.Since(start)
+ watchSendLoopWatchStreamDuration.Observe(totalDur.Seconds())
+ watchSendLoopWatchStreamDurationPerEvent.Observe(totalDur.Seconds() / float64(len(evs)))
+
case c, ok := <-sws.ctrlStream:
if !ok {
return
}
-
+ start := time.Now()
if err := sws.gRPCStream.Send(c); err != nil {
if isClientCtxErr(sws.gRPCStream.Context().Err(), err) {
sws.lg.Debug("failed to send watch control response to gRPC stream", zap.Error(err))
@@ -533,7 +552,11 @@ func (sws *serverWatchStream) sendLoop() {
delete(pending, wid)
}
+ watchSendLoopControlStreamDuration.Observe(time.Since(start).Seconds())
+
case <-progressTicker.C:
+ start := time.Now()
+
sws.mu.Lock()
for id, ok := range sws.progress {
if ok {
@@ -542,6 +565,7 @@ func (sws *serverWatchStream) sendLoop() {
sws.progress[id] = true
}
sws.mu.Unlock()
+ watchSendLoopProgressDuration.Observe(time.Since(start).Seconds())
case <-sws.closec:
return
@@ -549,8 +573,8 @@ func (sws *serverWatchStream) sendLoop() {
}
}
-func IsCreateEvent(e mvccpb.Event) bool {
- return e.Type == mvccpb.PUT && e.Kv.CreateRevision == e.Kv.ModRevision
+func IsCreateEvent(e *mvccpb.Event) bool {
+ return e.Type == mvccpb.Event_PUT && e.Kv.CreateRevision == e.Kv.ModRevision
}
func sendFragments(
@@ -560,20 +584,36 @@ func sendFragments(
) error {
// no need to fragment if total request size is smaller
// than max request limit or response contains only one event
- if uint(wr.Size()) < maxRequestBytes || len(wr.Events) < 2 {
+ if uint(proto.Size(wr)) < maxRequestBytes || len(wr.Events) < 2 {
return sendFunc(wr)
}
- ow := *wr
- ow.Events = make([]*mvccpb.Event, 0)
- ow.Fragment = true
-
var idx int
for {
- cur := ow
+ // Keep this explicit field copy in sync with pb.WatchResponse.
+ // TestWatchResponseProtoFieldCount guards against missing new fields.
+ //
+ // Header is the same for all fragments from one response, so
+ // it is safe to reuse. However, we cannot reuse wr itself.
+ // sendFunc can enqueue the response and return immediately,
+ // so the actual send may happen later. Reusing wr would let
+ // mutations in the next loop iteration corrupt queued fragments.
+ //
+ // REF: https://github.com/grpc/grpc-go/issues/5857
+ cur := &pb.WatchResponse{
+ Header: wr.Header,
+ WatchId: wr.WatchId,
+ Created: wr.Created,
+ Canceled: wr.Canceled,
+ CompactRevision: wr.CompactRevision,
+ CancelReason: wr.CancelReason,
+ Fragment: true,
+ Events: make([]*mvccpb.Event, 0),
+ }
+
for _, ev := range wr.Events[idx:] {
cur.Events = append(cur.Events, ev)
- if len(cur.Events) > 1 && uint(cur.Size()) >= maxRequestBytes {
+ if len(cur.Events) > 1 && uint(proto.Size(cur)) >= maxRequestBytes {
cur.Events = cur.Events[:len(cur.Events)-1]
break
}
@@ -583,7 +623,7 @@ func sendFragments(
// last response has no more fragment
cur.Fragment = false
}
- if err := sendFunc(&cur); err != nil {
+ if err := sendFunc(cur); err != nil {
return err
}
if !cur.Fragment {
@@ -608,12 +648,12 @@ func (sws *serverWatchStream) newResponseHeader(rev int64) *pb.ResponseHeader {
}
}
-func filterNoDelete(e mvccpb.Event) bool {
- return e.Type == mvccpb.DELETE
+func filterNoDelete(e *mvccpb.Event) bool {
+ return e.Type == mvccpb.Event_DELETE
}
-func filterNoPut(e mvccpb.Event) bool {
- return e.Type == mvccpb.PUT
+func filterNoPut(e *mvccpb.Event) bool {
+ return e.Type == mvccpb.Event_PUT
}
// FiltersFromRequest returns "mvcc.FilterFunc" from a given watch create request.
diff --git a/server/etcdserver/api/v3rpc/watch_test.go b/server/etcdserver/api/v3rpc/watch_test.go
index caa86f91ad71..1ecd31fae9bc 100644
--- a/server/etcdserver/api/v3rpc/watch_test.go
+++ b/server/etcdserver/api/v3rpc/watch_test.go
@@ -18,6 +18,7 @@ import (
"bytes"
"errors"
"math"
+ "reflect"
"testing"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
@@ -83,6 +84,26 @@ func TestSendFragment(t *testing.T) {
}
}
+func TestWatchResponseProtoFieldCount(t *testing.T) {
+ const expectedWatchResponseProtoFields = 8
+
+ fields := 0
+ typ := reflect.TypeOf(pb.WatchResponse{})
+ for i := 0; i < typ.NumField(); i++ {
+ if typ.Field(i).Tag.Get("protobuf") != "" {
+ fields++
+ }
+ }
+
+ // NOTE:
+ //
+ // We do manually value-copy in sendFragments. If there is new
+ // protobuf field added to WatchResponse, we need to update sendFragments.
+ if fields != expectedWatchResponseProtoFields {
+ t.Fatalf("unexpected pb.WatchResponse protobuf field count, got=%d expected=%d", fields, expectedWatchResponseProtoFields)
+ }
+}
+
func createResponse(dataSize, events int) (resp *pb.WatchResponse) {
resp = &pb.WatchResponse{Events: make([]*mvccpb.Event, events)}
for i := range resp.Events {
diff --git a/server/etcdserver/apply/apply.go b/server/etcdserver/apply/apply.go
index e45d53e17b12..935737c5aefa 100644
--- a/server/etcdserver/apply/apply.go
+++ b/server/etcdserver/apply/apply.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The etcd Authors
+// Copyright 2025 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,479 +15,39 @@
package apply
import (
- "context"
-
- "github.com/coreos/go-semver/semver"
- "github.com/gogo/protobuf/proto"
"go.uber.org/zap"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/api/v3/membershippb"
- "go.etcd.io/etcd/client/pkg/v3/types"
- "go.etcd.io/etcd/pkg/v3/traceutil"
- "go.etcd.io/etcd/server/v3/auth"
- "go.etcd.io/etcd/server/v3/etcdserver/api"
+ "go.etcd.io/etcd/pkg/v3/pbutil"
+ "go.etcd.io/etcd/pkg/v3/wait"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
- "go.etcd.io/etcd/server/v3/etcdserver/cindex"
- "go.etcd.io/etcd/server/v3/etcdserver/errors"
- mvcctxn "go.etcd.io/etcd/server/v3/etcdserver/txn"
- "go.etcd.io/etcd/server/v3/etcdserver/version"
- "go.etcd.io/etcd/server/v3/lease"
- serverstorage "go.etcd.io/etcd/server/v3/storage"
- "go.etcd.io/etcd/server/v3/storage/backend"
- "go.etcd.io/etcd/server/v3/storage/mvcc"
-)
-
-const (
- v3Version = "v3"
+ "go.etcd.io/raft/v3/raftpb"
)
-// RaftStatusGetter represents etcd server and Raft progress.
-type RaftStatusGetter interface {
- MemberID() types.ID
- Leader() types.ID
- CommittedIndex() uint64
- AppliedIndex() uint64
- Term() uint64
-}
-
-type Result struct {
- Resp proto.Message
- Err error
- // Physc signals the physical effect of the request has completed in addition
- // to being logically reflected by the node. Currently, only used for
- // Compaction requests.
- Physc <-chan struct{}
- Trace *traceutil.Trace
-}
-
-type applyFunc func(r *pb.InternalRaftRequest) *Result
-
-// applierV3 is the interface for processing V3 raft messages
-type applierV3 interface {
- // Apply executes the generic portion of application logic for the current applier, but
- // delegates the actual execution to the applyFunc method.
- Apply(r *pb.InternalRaftRequest, applyFunc applyFunc) *Result
-
- Put(p *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error)
- Range(r *pb.RangeRequest) (*pb.RangeResponse, *traceutil.Trace, error)
- DeleteRange(dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, *traceutil.Trace, error)
- Txn(rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error)
- Compaction(compaction *pb.CompactionRequest) (*pb.CompactionResponse, <-chan struct{}, *traceutil.Trace, error)
-
- LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error)
- LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error)
-
- LeaseCheckpoint(lc *pb.LeaseCheckpointRequest) (*pb.LeaseCheckpointResponse, error)
-
- Alarm(*pb.AlarmRequest) (*pb.AlarmResponse, error)
-
- Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error)
-
- AuthEnable() (*pb.AuthEnableResponse, error)
- AuthDisable() (*pb.AuthDisableResponse, error)
- AuthStatus() (*pb.AuthStatusResponse, error)
-
- UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
- UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
- UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
- UserGrantRole(ua *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)
- UserGet(ua *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)
- UserRevokeRole(ua *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)
- RoleAdd(ua *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
- RoleGrantPermission(ua *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)
- RoleGet(ua *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)
- RoleRevokePermission(ua *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)
- RoleDelete(ua *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)
- UserList(ua *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)
- RoleList(ua *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)
-}
-
-type SnapshotServer interface {
- ForceSnapshot()
-}
-
-type applierV3backend struct {
- lg *zap.Logger
- kv mvcc.KV
- alarmStore *v3alarm.AlarmStore
- authStore auth.AuthStore
- lessor lease.Lessor
- cluster *membership.RaftCluster
- raftStatus RaftStatusGetter
- snapshotServer SnapshotServer
- consistentIndex cindex.ConsistentIndexer
-
- txnModeWriteWithSharedBuffer bool
-}
-
-func newApplierV3Backend(
- lg *zap.Logger,
- kv mvcc.KV,
- alarmStore *v3alarm.AlarmStore,
- authStore auth.AuthStore,
- lessor lease.Lessor,
- cluster *membership.RaftCluster,
- raftStatus RaftStatusGetter,
- snapshotServer SnapshotServer,
- consistentIndex cindex.ConsistentIndexer,
- txnModeWriteWithSharedBuffer bool,
-) applierV3 {
- return &applierV3backend{
- lg: lg,
- kv: kv,
- alarmStore: alarmStore,
- authStore: authStore,
- lessor: lessor,
- cluster: cluster,
- raftStatus: raftStatus,
- snapshotServer: snapshotServer,
- consistentIndex: consistentIndex,
- txnModeWriteWithSharedBuffer: txnModeWriteWithSharedBuffer,
- }
-}
-
-func (a *applierV3backend) Apply(r *pb.InternalRaftRequest, applyFunc applyFunc) *Result {
- return applyFunc(r)
-}
-
-func (a *applierV3backend) Put(p *pb.PutRequest) (resp *pb.PutResponse, trace *traceutil.Trace, err error) {
- return mvcctxn.Put(context.TODO(), a.lg, a.lessor, a.kv, p)
-}
-
-func (a *applierV3backend) DeleteRange(dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, *traceutil.Trace, error) {
- return mvcctxn.DeleteRange(context.TODO(), a.lg, a.kv, dr)
-}
-
-func (a *applierV3backend) Range(r *pb.RangeRequest) (*pb.RangeResponse, *traceutil.Trace, error) {
- return mvcctxn.Range(context.TODO(), a.lg, a.kv, r)
-}
-
-func (a *applierV3backend) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) {
- return mvcctxn.Txn(context.TODO(), a.lg, rt, a.txnModeWriteWithSharedBuffer, a.kv, a.lessor)
-}
-
-func (a *applierV3backend) Compaction(compaction *pb.CompactionRequest) (*pb.CompactionResponse, <-chan struct{}, *traceutil.Trace, error) {
- resp := &pb.CompactionResponse{}
- resp.Header = &pb.ResponseHeader{}
- trace := traceutil.New("compact",
- a.lg,
- traceutil.Field{Key: "revision", Value: compaction.Revision},
- )
-
- ch, err := a.kv.Compact(trace, compaction.Revision)
- if err != nil {
- return nil, ch, nil, err
- }
- // get the current revision. which key to get is not important.
- rr, _ := a.kv.Range(context.TODO(), []byte("compaction"), nil, mvcc.RangeOptions{})
- resp.Header.Revision = rr.Rev
- return resp, ch, trace, err
-}
-
-func (a *applierV3backend) LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
- l, err := a.lessor.Grant(lease.LeaseID(lc.ID), lc.TTL)
- resp := &pb.LeaseGrantResponse{}
- if err == nil {
- resp.ID = int64(l.ID)
- resp.TTL = l.TTL()
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
- err := a.lessor.Revoke(lease.LeaseID(lc.ID))
- return &pb.LeaseRevokeResponse{Header: a.newHeader()}, err
-}
-
-func (a *applierV3backend) LeaseCheckpoint(lc *pb.LeaseCheckpointRequest) (*pb.LeaseCheckpointResponse, error) {
- for _, c := range lc.Checkpoints {
- err := a.lessor.Checkpoint(lease.LeaseID(c.ID), c.Remaining_TTL)
- if err != nil {
- return &pb.LeaseCheckpointResponse{Header: a.newHeader()}, err
- }
- }
- return &pb.LeaseCheckpointResponse{Header: a.newHeader()}, nil
-}
-
-func (a *applierV3backend) Alarm(ar *pb.AlarmRequest) (*pb.AlarmResponse, error) {
- resp := &pb.AlarmResponse{}
-
- switch ar.Action {
- case pb.AlarmRequest_GET:
- resp.Alarms = a.alarmStore.Get(ar.Alarm)
- case pb.AlarmRequest_ACTIVATE:
- if ar.Alarm == pb.AlarmType_NONE {
- break
- }
- m := a.alarmStore.Activate(types.ID(ar.MemberID), ar.Alarm)
- if m == nil {
- break
- }
- resp.Alarms = append(resp.Alarms, m)
- alarms.WithLabelValues(types.ID(ar.MemberID).String(), m.Alarm.String()).Inc()
- case pb.AlarmRequest_DEACTIVATE:
- m := a.alarmStore.Deactivate(types.ID(ar.MemberID), ar.Alarm)
- if m == nil {
- break
- }
- resp.Alarms = append(resp.Alarms, m)
- alarms.WithLabelValues(types.ID(ar.MemberID).String(), m.Alarm.String()).Dec()
- default:
- return nil, nil
- }
- return resp, nil
-}
-
-type applierV3Capped struct {
- applierV3
- q serverstorage.BackendQuota
-}
-
-// newApplierV3Capped creates an applyV3 that will reject Puts and transactions
-// with Puts so that the number of keys in the store is capped.
-func newApplierV3Capped(base applierV3) applierV3 { return &applierV3Capped{applierV3: base} }
-
-func (a *applierV3Capped) Put(_ *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) {
- return nil, nil, errors.ErrNoSpace
-}
-
-func (a *applierV3Capped) Txn(r *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) {
- if a.q.Cost(r) > 0 {
- return nil, nil, errors.ErrNoSpace
- }
- return a.applierV3.Txn(r)
-}
-
-func (a *applierV3Capped) LeaseGrant(_ *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
- return nil, errors.ErrNoSpace
-}
-
-func (a *applierV3backend) AuthEnable() (*pb.AuthEnableResponse, error) {
- err := a.authStore.AuthEnable()
- if err != nil {
- return nil, err
- }
- return &pb.AuthEnableResponse{Header: a.newHeader()}, nil
-}
-
-func (a *applierV3backend) AuthDisable() (*pb.AuthDisableResponse, error) {
- a.authStore.AuthDisable()
- return &pb.AuthDisableResponse{Header: a.newHeader()}, nil
-}
-
-func (a *applierV3backend) AuthStatus() (*pb.AuthStatusResponse, error) {
- enabled := a.authStore.IsAuthEnabled()
- authRevision := a.authStore.Revision()
- return &pb.AuthStatusResponse{Header: a.newHeader(), Enabled: enabled, AuthRevision: authRevision}, nil
-}
-
-func (a *applierV3backend) Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error) {
- ctx := context.WithValue(context.WithValue(context.Background(), auth.AuthenticateParamIndex{}, a.consistentIndex.ConsistentIndex()), auth.AuthenticateParamSimpleTokenPrefix{}, r.SimpleToken)
- resp, err := a.authStore.Authenticate(ctx, r.Name, r.Password)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
- resp, err := a.authStore.UserAdd(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) {
- resp, err := a.authStore.UserDelete(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) {
- resp, err := a.authStore.UserChangePassword(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) {
- resp, err := a.authStore.UserGrantRole(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
- resp, err := a.authStore.UserGet(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
- resp, err := a.authStore.UserRevokeRole(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
- resp, err := a.authStore.RoleAdd(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
- resp, err := a.authStore.RoleGrantPermission(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
- resp, err := a.authStore.RoleGet(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
+func Apply(lg *zap.Logger, e *raftpb.Entry, uberApply UberApplier, w wait.Wait, shouldApplyV3 membership.ShouldApplyV3) (ar *Result, id uint64) {
+ var raftReq pb.InternalRaftRequest
+ pbutil.MustUnmarshalMessage(&raftReq, e.Data)
+ lg.Debug("Apply", zap.Stringer("raftReq", &raftReq))
-func (a *applierV3backend) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
- resp, err := a.authStore.RoleRevokePermission(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {
- resp, err := a.authStore.RoleDelete(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
- resp, err := a.authStore.UserList(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-func (a *applierV3backend) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
- resp, err := a.authStore.RoleList(r)
- if resp != nil {
- resp.Header = a.newHeader()
- }
- return resp, err
-}
-
-type applierMembership struct {
- lg *zap.Logger
- cluster *membership.RaftCluster
- snapshotServer SnapshotServer
-}
-
-func NewApplierMembership(lg *zap.Logger, cluster *membership.RaftCluster, snapshotServer SnapshotServer) *applierMembership {
- return &applierMembership{
- lg: lg,
- cluster: cluster,
- snapshotServer: snapshotServer,
- }
-}
-
-func (a *applierMembership) ClusterVersionSet(r *membershippb.ClusterVersionSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
- prevVersion := a.cluster.Version()
- newVersion := semver.Must(semver.NewVersion(r.Ver))
- a.cluster.SetVersion(newVersion, api.UpdateCapability, shouldApplyV3)
- // Force snapshot after cluster version downgrade.
- if prevVersion != nil && newVersion.LessThan(*prevVersion) {
- lg := a.lg
- if lg != nil {
- lg.Info("Cluster version downgrade detected, forcing snapshot",
- zap.String("prev-cluster-version", prevVersion.String()),
- zap.String("new-cluster-version", newVersion.String()),
- )
+ id = raftReq.ID
+ if id == 0 {
+ if raftReq.Header == nil {
+ lg.Panic("Apply, could not find a header")
}
- a.snapshotServer.ForceSnapshot()
+ id = raftReq.Header.ID
}
-}
-
-func (a *applierMembership) ClusterMemberAttrSet(r *membershippb.ClusterMemberAttrSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
- a.cluster.UpdateAttributes(
- types.ID(r.Member_ID),
- membership.Attributes{
- Name: r.MemberAttributes.Name,
- ClientURLs: r.MemberAttributes.ClientUrls,
- },
- shouldApplyV3,
- )
-}
-
-func (a *applierMembership) DowngradeInfoSet(r *membershippb.DowngradeInfoSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
- d := version.DowngradeInfo{Enabled: false}
- if r.Enabled {
- d = version.DowngradeInfo{Enabled: true, TargetVersion: r.Ver}
- }
- a.cluster.SetDowngradeInfo(&d, shouldApplyV3)
-}
-
-type quotaApplierV3 struct {
- applierV3
- q serverstorage.Quota
-}
-func newQuotaApplierV3(lg *zap.Logger, quotaBackendBytesCfg int64, be backend.Backend, app applierV3) applierV3 {
- return "aApplierV3{app, serverstorage.NewBackendQuota(lg, quotaBackendBytesCfg, be, "v3-applier")}
-}
-
-func (a *quotaApplierV3) Put(p *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) {
- ok := a.q.Available(p)
- resp, trace, err := a.applierV3.Put(p)
- if err == nil && !ok {
- err = errors.ErrNoSpace
- }
- return resp, trace, err
-}
-
-func (a *quotaApplierV3) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) {
- ok := a.q.Available(rt)
- resp, trace, err := a.applierV3.Txn(rt)
- if err == nil && !ok {
- err = errors.ErrNoSpace
+ needResult := w.IsRegistered(id)
+ wrapper := &InternalRaftRequestWrapper{
+ InternalRaftRequest: &raftReq,
+ SkipRangeExecution: !needResult && raftReq.Txn != nil,
}
- return resp, trace, err
-}
-
-func (a *quotaApplierV3) LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
- ok := a.q.Available(lc)
- resp, err := a.applierV3.LeaseGrant(lc)
- if err == nil && !ok {
- err = errors.ErrNoSpace
+ if needResult || !noSideEffect(&raftReq) {
+ return uberApply.Apply(wrapper, shouldApplyV3), id
}
- return resp, err
+ return nil, id
}
-func (a *applierV3backend) newHeader() *pb.ResponseHeader {
- return &pb.ResponseHeader{
- ClusterId: uint64(a.cluster.ID()),
- MemberId: uint64(a.raftStatus.MemberID()),
- Revision: a.kv.Rev(),
- RaftTerm: a.raftStatus.Term(),
- }
+func noSideEffect(r *pb.InternalRaftRequest) bool {
+ return r.Range != nil || r.AuthUserGet != nil || r.AuthRoleGet != nil || r.AuthStatus != nil
}
diff --git a/server/etcdserver/apply/apply_auth.go b/server/etcdserver/apply/auth.go
similarity index 59%
rename from server/etcdserver/apply/apply_auth.go
rename to server/etcdserver/apply/auth.go
index 3922deebd012..2b6a46ae699e 100644
--- a/server/etcdserver/apply/apply_auth.go
+++ b/server/etcdserver/apply/auth.go
@@ -20,7 +20,7 @@ import (
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/pkg/v3/traceutil"
"go.etcd.io/etcd/server/v3/auth"
- "go.etcd.io/etcd/server/v3/etcdserver/txn"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/lease"
)
@@ -40,7 +40,7 @@ func newAuthApplierV3(as auth.AuthStore, base applierV3, lessor lease.Lessor) *a
return &authApplierV3{applierV3: base, as: as, lessor: lessor}
}
-func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest, applyFunc applyFunc) *Result {
+func (aa *authApplierV3) Apply(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3, applyFunc applyFunc) *Result {
aa.mu.Lock()
defer aa.mu.Unlock()
if r.Header != nil {
@@ -49,39 +49,48 @@ func (aa *authApplierV3) Apply(r *pb.InternalRaftRequest, applyFunc applyFunc) *
aa.authInfo.Username = r.Header.Username
aa.authInfo.Revision = r.Header.AuthRevision
}
- if needAdminPermission(r) {
+ if needAdminPermission(r.InternalRaftRequest) {
if err := aa.as.IsAdminPermitted(&aa.authInfo); err != nil {
aa.authInfo.Username = ""
aa.authInfo.Revision = 0
return &Result{Err: err}
}
}
- ret := aa.applierV3.Apply(r, applyFunc)
+ ret := aa.applierV3.Apply(r, shouldApplyV3, applyFunc)
aa.authInfo.Username = ""
aa.authInfo.Revision = 0
return ret
}
func (aa *authApplierV3) Put(r *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) {
- if err := aa.as.IsPutPermitted(&aa.authInfo, r.Key); err != nil {
+ if err := checkPutAuth(aa.as, &aa.authInfo, aa.lessor, r); err != nil {
return nil, nil, err
}
- if err := aa.checkLeasePuts(lease.LeaseID(r.Lease)); err != nil {
+ return aa.applierV3.Put(r)
+}
+
+func checkPutAuth(as auth.AuthStore, ai *auth.AuthInfo, lessor lease.Lessor, r *pb.PutRequest) error {
+ if err := as.IsPutPermitted(ai, r.Key); err != nil {
+ return err
+ }
+
+ if err := checkLeasePuts(as, ai, lessor, lease.LeaseID(r.Lease)); err != nil {
// The specified lease is already attached with a key that cannot
// be written by this user. It means the user cannot revoke the
// lease so attaching the lease to the newly written key should
// be forbidden.
- return nil, nil, err
+ return err
}
if r.PrevKv {
- err := aa.as.IsRangePermitted(&aa.authInfo, r.Key, nil)
+ err := as.IsRangePermitted(ai, r.Key, nil)
if err != nil {
- return nil, nil, err
+ return err
}
}
- return aa.applierV3.Put(r)
+
+ return nil
}
func (aa *authApplierV3) Range(r *pb.RangeRequest) (*pb.RangeResponse, *traceutil.Trace, error) {
@@ -105,38 +114,105 @@ func (aa *authApplierV3) DeleteRange(r *pb.DeleteRangeRequest) (*pb.DeleteRangeR
return aa.applierV3.DeleteRange(r)
}
-func (aa *authApplierV3) Txn(rt *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) {
- if err := txn.CheckTxnAuth(aa.as, &aa.authInfo, rt); err != nil {
+func (aa *authApplierV3) Txn(rt *pb.TxnRequest, skipRangeExecution bool) (*pb.TxnResponse, *traceutil.Trace, error) {
+ if err := CheckTxnAuth(aa.as, &aa.authInfo, aa.lessor, rt); err != nil {
return nil, nil, err
}
- return aa.applierV3.Txn(rt)
+ return aa.applierV3.Txn(rt, skipRangeExecution)
+}
+
+func CheckTxnAuth(as auth.AuthStore, ai *auth.AuthInfo, lessor lease.Lessor, rt *pb.TxnRequest) error {
+ return checkTxnPermission(as, ai, lessor, rt)
+}
+
+func checkTxnPermission(as auth.AuthStore, ai *auth.AuthInfo, lessor lease.Lessor, rt *pb.TxnRequest) error {
+ for _, c := range rt.Compare {
+ if err := as.IsRangePermitted(ai, c.Key, c.RangeEnd); err != nil {
+ return err
+ }
+ }
+ if err := checkTxnReqsPermission(as, ai, lessor, rt.Success); err != nil {
+ return err
+ }
+ return checkTxnReqsPermission(as, ai, lessor, rt.Failure)
+}
+
+func checkTxnReqsPermission(as auth.AuthStore, ai *auth.AuthInfo, lessor lease.Lessor, reqs []*pb.RequestOp) error {
+ for _, requ := range reqs {
+ switch tv := requ.Request.(type) {
+ case *pb.RequestOp_RequestRange:
+ if tv.RequestRange == nil {
+ continue
+ }
+
+ if err := as.IsRangePermitted(ai, tv.RequestRange.Key, tv.RequestRange.RangeEnd); err != nil {
+ return err
+ }
+
+ case *pb.RequestOp_RequestPut:
+ if tv.RequestPut == nil {
+ continue
+ }
+
+ if err := checkPutAuth(as, ai, lessor, tv.RequestPut); err != nil {
+ return err
+ }
+ case *pb.RequestOp_RequestDeleteRange:
+ if tv.RequestDeleteRange == nil {
+ continue
+ }
+
+ if tv.RequestDeleteRange.PrevKv {
+ err := as.IsRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
+ if err != nil {
+ return err
+ }
+ }
+
+ err := as.IsDeleteRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
+ if err != nil {
+ return err
+ }
+ case *pb.RequestOp_RequestTxn:
+ if tv.RequestTxn == nil {
+ continue
+ }
+
+ err := checkTxnPermission(as, ai, lessor, tv.RequestTxn)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
}
func (aa *authApplierV3) LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
- if err := aa.checkLeasePuts(lease.LeaseID(lc.ID)); err != nil {
+ if err := checkLeasePuts(aa.as, &aa.authInfo, aa.lessor, lease.LeaseID(lc.ID)); err != nil {
return nil, err
}
return aa.applierV3.LeaseRevoke(lc)
}
-func (aa *authApplierV3) checkLeasePuts(leaseID lease.LeaseID) error {
- l := aa.lessor.Lookup(leaseID)
+func checkLeasePuts(as auth.AuthStore, ai *auth.AuthInfo, lessor lease.Lessor, leaseID lease.LeaseID) error {
+ l := lessor.Lookup(leaseID)
if l != nil {
- return aa.checkLeasePutsKeys(l)
+ return checkLeasePutsKeys(as, ai, l)
}
return nil
}
-func (aa *authApplierV3) checkLeasePutsKeys(l *lease.Lease) error {
+func checkLeasePutsKeys(as auth.AuthStore, ai *auth.AuthInfo, l *lease.Lease) error {
// early return for most-common scenario of either disabled auth or admin user.
// IsAdminPermitted also checks whether auth is enabled
- if err := aa.as.IsAdminPermitted(&aa.authInfo); err == nil {
+ if err := as.IsAdminPermitted(ai); err == nil {
return nil
}
for _, key := range l.Keys() {
- if err := aa.as.IsPutPermitted(&aa.authInfo, []byte(key)); err != nil {
+ if err := as.IsPutPermitted(ai, []byte(key)); err != nil {
return err
}
}
diff --git a/server/etcdserver/apply/apply_auth_test.go b/server/etcdserver/apply/auth_test.go
similarity index 58%
rename from server/etcdserver/apply/apply_auth_test.go
rename to server/etcdserver/apply/auth_test.go
index cbd1893266c5..9e6faffea49c 100644
--- a/server/etcdserver/apply/apply_auth_test.go
+++ b/server/etcdserver/apply/auth_test.go
@@ -33,6 +33,7 @@ import (
"go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/backend"
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
"go.etcd.io/etcd/server/v3/storage/mvcc"
"go.etcd.io/etcd/server/v3/storage/schema"
@@ -44,7 +45,7 @@ func dummyIndexWaiter(_ uint64) <-chan struct{} {
return ch
}
-func dummyApplyFunc(_ *pb.InternalRaftRequest) *Result {
+func dummyApplyFunc(_ *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3) *Result {
return &Result{}
}
@@ -98,18 +99,18 @@ func defaultAuthApplierV3(t *testing.T) *authApplierV3 {
consistentIndex := cindex.NewConsistentIndex(be)
return newAuthApplierV3(
authStore,
- newApplierV3Backend(
- lg,
- kv,
- alarmStore,
- authStore,
- lessor,
- cluster,
- &fakeRaftStatusGetter{},
- &fakeSnapshotServer{},
- consistentIndex,
- false,
- ),
+ newApplierV3Backend(ApplierOptions{
+ Logger: lg,
+ KV: kv,
+ AlarmStore: alarmStore,
+ ConsistentIndex: consistentIndex,
+ AuthStore: authStore,
+ Lessor: lessor,
+ Cluster: cluster,
+ RaftStatus: &fakeRaftStatusGetter{},
+ SnapshotServer: &fakeSnapshotServer{},
+ TxnModeWriteWithSharedBuffer: false,
+ }),
lessor)
}
@@ -143,7 +144,7 @@ func mustCreateRolesAndEnableAuth(t *testing.T, authApplier *authApplierV3) {
_, err = authApplier.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: userReadOnly, Role: roleReadOnly})
require.NoError(t, err)
_, err = authApplier.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{Name: roleReadOnly, Perm: &authpb.Permission{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte(key),
RangeEnd: []byte(rangeEnd),
}})
@@ -156,7 +157,7 @@ func mustCreateRolesAndEnableAuth(t *testing.T, authApplier *authApplierV3) {
_, err = authApplier.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: userWriteOnly, Role: roleWriteOnly})
require.NoError(t, err)
_, err = authApplier.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{Name: roleWriteOnly, Perm: &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte(key),
RangeEnd: []byte(rangeEnd),
}})
@@ -179,36 +180,36 @@ func setAuthInfo(authApplier *authApplierV3, userName string) {
func TestAuthApplierV3_Apply(t *testing.T) {
tcs := []struct {
name string
- request *pb.InternalRaftRequest
+ request *InternalRaftRequestWrapper
expectResult *Result
}{
{
name: "request does not need admin permission",
- request: &pb.InternalRaftRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
- },
+ }},
expectResult: &Result{},
},
{
name: "request needs admin permission but permission denied",
- request: &pb.InternalRaftRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{
Username: userReadOnly,
},
AuthEnable: &pb.AuthEnableRequest{},
- },
+ }},
expectResult: &Result{
Err: auth.ErrPermissionDenied,
},
},
{
name: "request needs admin permission and permitted",
- request: &pb.InternalRaftRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{
Username: userRoot,
},
AuthEnable: &pb.AuthEnableRequest{},
- },
+ }},
expectResult: &Result{},
},
}
@@ -217,7 +218,7 @@ func TestAuthApplierV3_Apply(t *testing.T) {
mustCreateRolesAndEnableAuth(t, authApplier)
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- result := authApplier.Apply(tc.request, dummyApplyFunc)
+ result := authApplier.Apply(tc.request, membership.ApplyBoth, dummyApplyFunc)
require.Equalf(t, result, tc.expectResult, "Apply: got %v, expect: %v", result, tc.expectResult)
})
}
@@ -228,92 +229,92 @@ func TestAuthApplierV3_Apply(t *testing.T) {
func TestAuthApplierV3_AdminPermission(t *testing.T) {
tcs := []struct {
name string
- request *pb.InternalRaftRequest
+ request *InternalRaftRequestWrapper
adminPermissionNeeded bool
}{
{
name: "Range does not need admin permission",
- request: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}}},
adminPermissionNeeded: false,
},
{
name: "Put does not need admin permission",
- request: &pb.InternalRaftRequest{Put: &pb.PutRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Put: &pb.PutRequest{}}},
adminPermissionNeeded: false,
},
{
name: "DeleteRange does not need admin permission",
- request: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}}},
adminPermissionNeeded: false,
},
{
name: "Txn does not need admin permission",
- request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}}},
adminPermissionNeeded: false,
},
{
name: "Compaction does not need admin permission",
- request: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}}},
adminPermissionNeeded: false,
},
{
name: "LeaseGrant does not need admin permission",
- request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}}},
adminPermissionNeeded: false,
},
{
name: "LeaseRevoke does not need admin permission",
- request: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}}},
adminPermissionNeeded: false,
},
{
name: "Alarm does not need admin permission",
- request: &pb.InternalRaftRequest{Alarm: &pb.AlarmRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Alarm: &pb.AlarmRequest{}}},
adminPermissionNeeded: false,
},
{
name: "LeaseCheckpoint does not need admin permission",
- request: &pb.InternalRaftRequest{LeaseCheckpoint: &pb.LeaseCheckpointRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseCheckpoint: &pb.LeaseCheckpointRequest{}}},
adminPermissionNeeded: false,
},
{
name: "Authenticate does not need admin permission",
- request: &pb.InternalRaftRequest{Authenticate: &pb.InternalAuthenticateRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Authenticate: &pb.InternalAuthenticateRequest{}}},
adminPermissionNeeded: false,
},
{
name: "ClusterVersionSet does not need admin permission",
- request: &pb.InternalRaftRequest{ClusterVersionSet: &membershippb.ClusterVersionSetRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{ClusterVersionSet: &membershippb.ClusterVersionSetRequest{}}},
adminPermissionNeeded: false,
},
{
name: "ClusterMemberAttrSet does not need admin permission",
- request: &pb.InternalRaftRequest{ClusterMemberAttrSet: &membershippb.ClusterMemberAttrSetRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{ClusterMemberAttrSet: &membershippb.ClusterMemberAttrSetRequest{}}},
adminPermissionNeeded: false,
},
{
name: "DowngradeInfoSet does not need admin permission",
- request: &pb.InternalRaftRequest{DowngradeInfoSet: &membershippb.DowngradeInfoSetRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{DowngradeInfoSet: &membershippb.DowngradeInfoSetRequest{}}},
adminPermissionNeeded: false,
},
{
name: "AuthUserGet does not need admin permission",
- request: &pb.InternalRaftRequest{AuthUserGet: &pb.AuthUserGetRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserGet: &pb.AuthUserGetRequest{}}},
adminPermissionNeeded: false,
},
{
name: "AuthRoleGet does not need admin permission",
- request: &pb.InternalRaftRequest{AuthRoleGet: &pb.AuthRoleGetRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleGet: &pb.AuthRoleGetRequest{}}},
adminPermissionNeeded: false,
},
{
name: "AuthEnable needs admin permission",
- request: &pb.InternalRaftRequest{AuthEnable: &pb.AuthEnableRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthEnable: &pb.AuthEnableRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthDisable needs admin permission",
- request: &pb.InternalRaftRequest{AuthDisable: &pb.AuthDisableRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthDisable: &pb.AuthDisableRequest{}}},
adminPermissionNeeded: true,
},
{
@@ -323,57 +324,57 @@ func TestAuthApplierV3_AdminPermission(t *testing.T) {
},
{
name: "AuthUserAdd needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserAdd: &pb.AuthUserAddRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserAdd: &pb.AuthUserAddRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthUserDelete needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserDelete: &pb.AuthUserDeleteRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserDelete: &pb.AuthUserDeleteRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthUserChangePassword needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserChangePassword: &pb.AuthUserChangePasswordRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserChangePassword: &pb.AuthUserChangePasswordRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthUserGrantRole needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserGrantRole: &pb.AuthUserGrantRoleRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserGrantRole: &pb.AuthUserGrantRoleRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthUserRevokeRole needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserRevokeRole: &pb.AuthUserRevokeRoleRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserRevokeRole: &pb.AuthUserRevokeRoleRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthUserList needs admin permission",
- request: &pb.InternalRaftRequest{AuthUserList: &pb.AuthUserListRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthUserList: &pb.AuthUserListRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthRoleList needs admin permission",
- request: &pb.InternalRaftRequest{AuthRoleList: &pb.AuthRoleListRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleList: &pb.AuthRoleListRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthRoleAdd needs admin permission",
- request: &pb.InternalRaftRequest{AuthRoleAdd: &pb.AuthRoleAddRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleAdd: &pb.AuthRoleAddRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthRoleDelete needs admin permission",
- request: &pb.InternalRaftRequest{AuthRoleDelete: &pb.AuthRoleDeleteRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleDelete: &pb.AuthRoleDeleteRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthRoleGrantPermission needs admin permission",
- request: &pb.InternalRaftRequest{AuthRoleGrantPermission: &pb.AuthRoleGrantPermissionRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleGrantPermission: &pb.AuthRoleGrantPermissionRequest{}}},
adminPermissionNeeded: true,
},
{
name: "AuthRoleRevokePermission needs admin permission",
- request: &pb.InternalRaftRequest{AuthRoleRevokePermission: &pb.AuthRoleRevokePermissionRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{AuthRoleRevokePermission: &pb.AuthRoleRevokePermissionRequest{}}},
adminPermissionNeeded: true,
},
}
@@ -384,7 +385,7 @@ func TestAuthApplierV3_AdminPermission(t *testing.T) {
if tc.adminPermissionNeeded {
tc.request.Header = &pb.RequestHeader{Username: userReadOnly}
}
- result := authApplier.Apply(tc.request, dummyApplyFunc)
+ result := authApplier.Apply(tc.request, membership.ApplyBoth, dummyApplyFunc)
require.Equalf(t, errors.Is(result.Err, auth.ErrPermissionDenied), tc.adminPermissionNeeded, "Admin permission needed")
})
}
@@ -657,7 +658,7 @@ func TestAuthApplierV3_Txn(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
setAuthInfo(authApplier, tc.userName)
- _, _, err := authApplier.Txn(tc.request)
+ _, _, err := authApplier.Txn(tc.request, false)
require.Equalf(t, tc.expectError, err, "Range returned unexpected error (or lack thereof), expected: %v, got: %v", tc.expectError, err)
})
}
@@ -785,24 +786,24 @@ func TestAuthApplierV3_RoleGet(t *testing.T) {
func TestCheckLeasePutsKeys(t *testing.T) {
aa := defaultAuthApplierV3(t)
- require.NoErrorf(t, aa.checkLeasePutsKeys(lease.NewLease(lease.LeaseID(1), 3600)), "auth is disabled, should allow puts")
+ require.NoErrorf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, lease.NewLease(lease.LeaseID(1), 3600)), "auth is disabled, should allow puts")
mustCreateRolesAndEnableAuth(t, aa)
aa.authInfo = auth.AuthInfo{Username: "root"}
- require.NoErrorf(t, aa.checkLeasePutsKeys(lease.NewLease(lease.LeaseID(1), 3600)), "auth is enabled, should allow puts for root")
+ require.NoErrorf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, lease.NewLease(lease.LeaseID(1), 3600)), "auth is enabled, should allow puts for root")
l := lease.NewLease(lease.LeaseID(1), 3600)
l.SetLeaseItem(lease.LeaseItem{Key: "a"})
aa.authInfo = auth.AuthInfo{Username: "bob", Revision: 0}
- require.ErrorIsf(t, aa.checkLeasePutsKeys(l), auth.ErrUserEmpty, "auth is enabled, should not allow bob, non existing at rev 0")
+ require.ErrorIsf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, l), auth.ErrUserEmpty, "auth is enabled, should not allow bob, non existing at rev 0")
aa.authInfo = auth.AuthInfo{Username: "bob", Revision: 1}
- require.ErrorIsf(t, aa.checkLeasePutsKeys(l), auth.ErrAuthOldRevision, "auth is enabled, old revision")
+ require.ErrorIsf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, l), auth.ErrAuthOldRevision, "auth is enabled, old revision")
aa.authInfo = auth.AuthInfo{Username: "bob", Revision: aa.as.Revision()}
- require.ErrorIsf(t, aa.checkLeasePutsKeys(l), auth.ErrPermissionDenied, "auth is enabled, bob does not have permissions, bob does not exist")
+ require.ErrorIsf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, l), auth.ErrPermissionDenied, "auth is enabled, bob does not have permissions, bob does not exist")
_, err := aa.as.UserAdd(&pb.AuthUserAddRequest{Name: "bob", Options: &authpb.UserAddOptions{NoPassword: true}})
require.NoErrorf(t, err, "bob should be added without error")
aa.authInfo = auth.AuthInfo{Username: "bob", Revision: aa.as.Revision()}
- require.ErrorIsf(t, aa.checkLeasePutsKeys(l), auth.ErrPermissionDenied, "auth is enabled, bob exists yet does not have permissions")
+ require.ErrorIsf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, l), auth.ErrPermissionDenied, "auth is enabled, bob exists yet does not have permissions")
// allow bob to access "a"
_, err = aa.as.RoleAdd(&pb.AuthRoleAddRequest{Name: "bobsrole"})
@@ -810,7 +811,7 @@ func TestCheckLeasePutsKeys(t *testing.T) {
_, err = aa.as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
Name: "bobsrole",
Perm: &authpb.Permission{
- PermType: authpb.READWRITE,
+ PermType: authpb.Permission_READWRITE,
Key: []byte("a"),
RangeEnd: nil,
},
@@ -823,5 +824,369 @@ func TestCheckLeasePutsKeys(t *testing.T) {
require.NoErrorf(t, err, "bob should be granted bobsrole without error")
aa.authInfo = auth.AuthInfo{Username: "bob", Revision: aa.as.Revision()}
- assert.NoErrorf(t, aa.checkLeasePutsKeys(l), "bob should be able to access key 'a'")
+ assert.NoErrorf(t, checkLeasePutsKeys(aa.as, &aa.authInfo, l), "bob should be able to access key 'a'")
+}
+
+func TestCheckTxnAuth(t *testing.T) {
+ be, _ := betesting.NewDefaultTmpBackend(t)
+ defer betesting.Close(t, be)
+ as := setupAuth(t, be)
+
+ tests := []struct {
+ name string
+ txnRequest *pb.TxnRequest
+ err error
+ }{
+ {
+ name: "Out of range compare is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Compare: []*pb.Compare{outOfRangeCompare},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "In range compare is authorized",
+ txnRequest: &pb.TxnRequest{
+ Compare: []*pb.Compare{inRangeCompare},
+ },
+ err: nil,
+ },
+ {
+ name: "Nil request range is always authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{nilRequestRange},
+ },
+ err: nil,
+ },
+ {
+ name: "Range request in range is authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestRange},
+ Failure: []*pb.RequestOp{inRangeRequestRange},
+ },
+ err: nil,
+ },
+ {
+ name: "Range request out of range success case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestRange},
+ Failure: []*pb.RequestOp{inRangeRequestRange},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Range request out of range failure case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestRange},
+ Failure: []*pb.RequestOp{outOfRangeRequestRange},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Nil Put request is always authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{nilRequestPut},
+ },
+ err: nil,
+ },
+ {
+ name: "Put request in range in authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestPut},
+ Failure: []*pb.RequestOp{inRangeRequestPut},
+ },
+ err: nil,
+ },
+ {
+ name: "Put request out of range success case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestPut},
+ Failure: []*pb.RequestOp{inRangeRequestPut},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Put request out of range failure case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestPut},
+ Failure: []*pb.RequestOp{outOfRangeRequestPut},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Nil delete request is authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{nilRequestDeleteRange},
+ },
+ err: nil,
+ },
+ {
+ name: "Delete range request in range is authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestDeleteRange},
+ Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
+ },
+ err: nil,
+ },
+ {
+ name: "Delete range request out of range success case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
+ Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Delete range request out of range failure case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestDeleteRange},
+ Failure: []*pb.RequestOp{outOfRangeRequestDeleteRange},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Delete range request out of range and PrevKv false success case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestDeleteRangeKvFalse},
+ Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Delete range request out of range and PrevKv false failure case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestDeleteRange},
+ Failure: []*pb.RequestOp{outOfRangeRequestDeleteRangeKvFalse},
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Nested txn request in range is authorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{inRangeRequestRange, inRangeRequestPut},
+ Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
+ },
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "Nested txn request out of range success case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestRange},
+ },
+ },
+ },
+ },
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Nested txn request out of range failure case is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Failure: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Failure: []*pb.RequestOp{outOfRangeRequestPut},
+ },
+ },
+ },
+ },
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Nested txn request out of range delete is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
+ },
+ },
+ },
+ },
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ {
+ name: "Two level nested txn request out of range delete is unauthorized",
+ txnRequest: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Failure: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ err: auth.ErrPermissionDenied,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := CheckTxnAuth(as, &auth.AuthInfo{Username: "foo", Revision: 8}, &lease.FakeLessor{}, tt.txnRequest)
+ assert.Equal(t, tt.err, err)
+ })
+ }
+}
+
+// CheckTxnAuth test setup.
+func setupAuth(t *testing.T, be backend.Backend) auth.AuthStore {
+ lg := zaptest.NewLogger(t)
+
+ simpleTokenTTLDefault := 300 * time.Second
+ tokenTypeSimple := "simple"
+ dummyIndexWaiter1 := func(index uint64) <-chan struct{} {
+ ch := make(chan struct{}, 1)
+ go func() {
+ ch <- struct{}{}
+ }()
+ return ch
+ }
+
+ tp, _ := auth.NewTokenProvider(zaptest.NewLogger(t), tokenTypeSimple, dummyIndexWaiter1, simpleTokenTTLDefault)
+
+ as := auth.NewAuthStore(lg, schema.NewAuthBackend(lg, be), tp, 4)
+
+ // create "root" user and "foo" user with limited range
+ _, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "root"})
+ require.NoError(t, err)
+
+ _, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "rw"})
+ require.NoError(t, err)
+
+ _, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
+ Name: "rw",
+ Perm: &authpb.Permission{
+ PermType: authpb.Permission_READWRITE,
+ Key: []byte("foo"),
+ RangeEnd: []byte("zoo"),
+ },
+ })
+ require.NoError(t, err)
+
+ _, err = as.UserAdd(&pb.AuthUserAddRequest{Name: "root", Password: "foo"})
+ require.NoError(t, err)
+
+ _, err = as.UserAdd(&pb.AuthUserAddRequest{Name: "foo", Password: "foo"})
+ require.NoError(t, err)
+
+ _, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "root", Role: "root"})
+ require.NoError(t, err)
+
+ _, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "rw"})
+ require.NoError(t, err)
+
+ err = as.AuthEnable()
+ require.NoError(t, err)
+
+ return as
}
+
+// CheckTxnAuth variables setup.
+var (
+ inRangeCompare = &pb.Compare{
+ Key: []byte("foo"),
+ RangeEnd: []byte("zoo"),
+ }
+ outOfRangeCompare = &pb.Compare{
+ Key: []byte("boo"),
+ RangeEnd: []byte("zoo"),
+ }
+ nilRequestPut = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: nil,
+ },
+ }
+ inRangeRequestPut = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: &pb.PutRequest{
+ Key: []byte("foo"),
+ },
+ },
+ }
+ outOfRangeRequestPut = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: &pb.PutRequest{
+ Key: []byte("boo"),
+ },
+ },
+ }
+ nilRequestRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: nil,
+ },
+ }
+ inRangeRequestRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: &pb.RangeRequest{
+ Key: []byte("foo"),
+ RangeEnd: []byte("zoo"),
+ },
+ },
+ }
+ outOfRangeRequestRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: &pb.RangeRequest{
+ Key: []byte("boo"),
+ RangeEnd: []byte("zoo"),
+ },
+ },
+ }
+ nilRequestDeleteRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestDeleteRange{
+ RequestDeleteRange: nil,
+ },
+ }
+ inRangeRequestDeleteRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestDeleteRange{
+ RequestDeleteRange: &pb.DeleteRangeRequest{
+ Key: []byte("foo"),
+ RangeEnd: []byte("zoo"),
+ PrevKv: true,
+ },
+ },
+ }
+ outOfRangeRequestDeleteRange = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestDeleteRange{
+ RequestDeleteRange: &pb.DeleteRangeRequest{
+ Key: []byte("boo"),
+ RangeEnd: []byte("zoo"),
+ PrevKv: true,
+ },
+ },
+ }
+ outOfRangeRequestDeleteRangeKvFalse = &pb.RequestOp{
+ Request: &pb.RequestOp_RequestDeleteRange{
+ RequestDeleteRange: &pb.DeleteRangeRequest{
+ Key: []byte("boo"),
+ RangeEnd: []byte("zoo"),
+ PrevKv: false,
+ },
+ },
+ }
+)
diff --git a/server/etcdserver/apply/backend.go b/server/etcdserver/apply/backend.go
new file mode 100644
index 000000000000..7d6d9b238260
--- /dev/null
+++ b/server/etcdserver/apply/backend.go
@@ -0,0 +1,313 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 apply
+
+import (
+ "context"
+
+ "github.com/coreos/go-semver/semver"
+ "go.uber.org/zap"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/membershippb"
+ "go.etcd.io/etcd/client/pkg/v3/types"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/auth"
+ "go.etcd.io/etcd/server/v3/etcdserver/api"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/membership"
+ mvcctxn "go.etcd.io/etcd/server/v3/etcdserver/txn"
+ "go.etcd.io/etcd/server/v3/etcdserver/version"
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+type applierV3backend struct {
+ options ApplierOptions
+}
+
+func newApplierV3Backend(opts ApplierOptions) applierV3 {
+ return &applierV3backend{
+ options: opts,
+ }
+}
+
+func (a *applierV3backend) Apply(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3, applyFunc applyFunc) *Result {
+ return applyFunc(r, shouldApplyV3)
+}
+
+func (a *applierV3backend) Put(p *pb.PutRequest) (resp *pb.PutResponse, trace *traceutil.Trace, err error) {
+ return mvcctxn.Put(context.TODO(), a.options.Logger, a.options.Lessor, a.options.KV, p)
+}
+
+func (a *applierV3backend) DeleteRange(dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, *traceutil.Trace, error) {
+ return mvcctxn.DeleteRange(context.TODO(), a.options.Logger, a.options.KV, dr)
+}
+
+func (a *applierV3backend) Range(r *pb.RangeRequest) (*pb.RangeResponse, *traceutil.Trace, error) {
+ return mvcctxn.Range(context.TODO(), a.options.Logger, a.options.KV, r, true)
+}
+
+func (a *applierV3backend) Txn(rt *pb.TxnRequest, skipRangeExecution bool) (*pb.TxnResponse, *traceutil.Trace, error) {
+ return mvcctxn.Txn(context.TODO(), a.options.Logger, rt, a.options.TxnModeWriteWithSharedBuffer, a.options.KV, a.options.Lessor, skipRangeExecution)
+}
+
+func (a *applierV3backend) Compaction(compaction *pb.CompactionRequest) (*pb.CompactionResponse, <-chan struct{}, *traceutil.Trace, error) {
+ resp := &pb.CompactionResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ ctx, trace := traceutil.EnsureTrace(context.TODO(), a.options.Logger, "compact",
+ traceutil.Field{Key: "revision", Value: compaction.Revision},
+ )
+
+ ch, err := a.options.KV.Compact(trace, compaction.Revision)
+ if err != nil {
+ return nil, ch, nil, err
+ }
+ // get the current revision. which key to get is not important.
+ rr, _ := a.options.KV.Range(ctx, []byte("compaction"), nil, mvcc.RangeOptions{})
+ resp.Header.Revision = rr.Rev
+ return resp, ch, trace, err
+}
+
+func (a *applierV3backend) LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
+ l, err := a.options.Lessor.Grant(lease.LeaseID(lc.ID), lc.TTL)
+ resp := &pb.LeaseGrantResponse{}
+ if err == nil {
+ resp.ID = int64(l.ID)
+ resp.TTL = l.TTL()
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
+ err := a.options.Lessor.Revoke(lease.LeaseID(lc.ID))
+ return &pb.LeaseRevokeResponse{Header: a.newHeader()}, err
+}
+
+func (a *applierV3backend) LeaseCheckpoint(lc *pb.LeaseCheckpointRequest) (*pb.LeaseCheckpointResponse, error) {
+ for _, c := range lc.Checkpoints {
+ err := a.options.Lessor.Checkpoint(lease.LeaseID(c.ID), c.Remaining_TTL)
+ if err != nil {
+ return &pb.LeaseCheckpointResponse{Header: a.newHeader()}, err
+ }
+ }
+ return &pb.LeaseCheckpointResponse{Header: a.newHeader()}, nil
+}
+
+func (a *applierV3backend) Alarm(ar *pb.AlarmRequest) (*pb.AlarmResponse, error) {
+ resp := &pb.AlarmResponse{}
+
+ switch ar.Action {
+ case pb.AlarmRequest_GET:
+ resp.Alarms = a.options.AlarmStore.Get(ar.Alarm)
+ case pb.AlarmRequest_ACTIVATE:
+ if ar.Alarm == pb.AlarmType_NONE {
+ break
+ }
+ m := a.options.AlarmStore.Activate(types.ID(ar.MemberID), ar.Alarm)
+ if m == nil {
+ break
+ }
+ resp.Alarms = append(resp.Alarms, m)
+ alarms.WithLabelValues(types.ID(ar.MemberID).String(), m.Alarm.String()).Inc()
+ case pb.AlarmRequest_DEACTIVATE:
+ m := a.options.AlarmStore.Deactivate(types.ID(ar.MemberID), ar.Alarm)
+ if m == nil {
+ break
+ }
+ resp.Alarms = append(resp.Alarms, m)
+ alarms.WithLabelValues(types.ID(ar.MemberID).String(), m.Alarm.String()).Dec()
+ default:
+ return nil, nil
+ }
+ return resp, nil
+}
+
+func (a *applierV3backend) AuthEnable() (*pb.AuthEnableResponse, error) {
+ err := a.options.AuthStore.AuthEnable()
+ if err != nil {
+ return nil, err
+ }
+ return &pb.AuthEnableResponse{Header: a.newHeader()}, nil
+}
+
+func (a *applierV3backend) AuthDisable() (*pb.AuthDisableResponse, error) {
+ a.options.AuthStore.AuthDisable()
+ return &pb.AuthDisableResponse{Header: a.newHeader()}, nil
+}
+
+func (a *applierV3backend) AuthStatus() (*pb.AuthStatusResponse, error) {
+ enabled := a.options.AuthStore.IsAuthEnabled()
+ authRevision := a.options.AuthStore.Revision()
+ return &pb.AuthStatusResponse{Header: a.newHeader(), Enabled: enabled, AuthRevision: authRevision}, nil
+}
+
+func (a *applierV3backend) Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error) {
+ ctx := context.WithValue(context.WithValue(context.Background(), auth.AuthenticateParamIndex{}, a.options.ConsistentIndex.ConsistentIndex()), auth.AuthenticateParamSimpleTokenPrefix{}, r.SimpleToken)
+ resp, err := a.options.AuthStore.Authenticate(ctx, r.Name, r.Password)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) {
+ resp, err := a.options.AuthStore.UserAdd(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) {
+ resp, err := a.options.AuthStore.UserDelete(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) {
+ resp, err := a.options.AuthStore.UserChangePassword(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) {
+ resp, err := a.options.AuthStore.UserGrantRole(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
+ resp, err := a.options.AuthStore.UserGet(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
+ resp, err := a.options.AuthStore.UserRevokeRole(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
+ resp, err := a.options.AuthStore.RoleAdd(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
+ resp, err := a.options.AuthStore.RoleGrantPermission(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
+ resp, err := a.options.AuthStore.RoleGet(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
+ resp, err := a.options.AuthStore.RoleRevokePermission(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {
+ resp, err := a.options.AuthStore.RoleDelete(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
+ resp, err := a.options.AuthStore.UserList(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
+ resp, err := a.options.AuthStore.RoleList(r)
+ if resp != nil {
+ resp.Header = a.newHeader()
+ }
+ return resp, err
+}
+
+func (a *applierV3backend) ClusterVersionSet(r *membershippb.ClusterVersionSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
+ prevVersion := a.options.Cluster.Version()
+ newVersion := semver.Must(semver.NewVersion(r.Ver))
+ a.options.Cluster.SetVersion(newVersion, api.UpdateCapability, shouldApplyV3)
+ // Force snapshot after cluster version downgrade.
+ if prevVersion != nil && newVersion.LessThan(*prevVersion) {
+ lg := a.options.Logger
+ if lg != nil {
+ lg.Info("Cluster version downgrade detected, forcing snapshot",
+ zap.String("prev-cluster-version", prevVersion.String()),
+ zap.String("new-cluster-version", newVersion.String()),
+ )
+ }
+ a.options.SnapshotServer.ForceSnapshot()
+ }
+}
+
+func (a *applierV3backend) ClusterMemberAttrSet(r *membershippb.ClusterMemberAttrSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
+ a.options.Cluster.UpdateAttributes(
+ types.ID(r.Member_ID),
+ membership.Attributes{
+ Name: r.MemberAttributes.Name,
+ ClientURLs: r.MemberAttributes.ClientUrls,
+ },
+ shouldApplyV3,
+ )
+}
+
+func (a *applierV3backend) DowngradeInfoSet(r *membershippb.DowngradeInfoSetRequest, shouldApplyV3 membership.ShouldApplyV3) {
+ d := version.DowngradeInfo{Enabled: false}
+ if r.Enabled {
+ d = version.DowngradeInfo{Enabled: true, TargetVersion: r.Ver}
+ }
+ a.options.Cluster.SetDowngradeInfo(&d, shouldApplyV3)
+}
+
+func (a *applierV3backend) newHeader() *pb.ResponseHeader {
+ return &pb.ResponseHeader{
+ ClusterId: uint64(a.options.Cluster.ID()),
+ MemberId: uint64(a.options.RaftStatus.MemberID()),
+ Revision: a.options.KV.Rev(),
+ RaftTerm: a.options.RaftStatus.Term(),
+ }
+}
diff --git a/server/etcdserver/apply/capped.go b/server/etcdserver/apply/capped.go
new file mode 100644
index 000000000000..d17367bb1c5a
--- /dev/null
+++ b/server/etcdserver/apply/capped.go
@@ -0,0 +1,46 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 apply
+
+import (
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/etcdserver/errors"
+ serverstorage "go.etcd.io/etcd/server/v3/storage"
+)
+
+type applierV3Capped struct {
+ applierV3
+ q serverstorage.BackendQuota
+}
+
+// newApplierV3Capped creates an applyV3 that will reject Puts and transactions
+// with Puts so that the number of keys in the store is capped.
+func newApplierV3Capped(base applierV3) applierV3 { return &applierV3Capped{applierV3: base} }
+
+func (a *applierV3Capped) Put(_ *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) {
+ return nil, nil, errors.ErrNoSpace
+}
+
+func (a *applierV3Capped) Txn(r *pb.TxnRequest, skipRangeExecution bool) (*pb.TxnResponse, *traceutil.Trace, error) {
+ if a.q.Cost(r) > 0 {
+ return nil, nil, errors.ErrNoSpace
+ }
+ return a.applierV3.Txn(r, skipRangeExecution)
+}
+
+func (a *applierV3Capped) LeaseGrant(_ *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
+ return nil, errors.ErrNoSpace
+}
diff --git a/server/etcdserver/apply/corrupt.go b/server/etcdserver/apply/corrupt.go
index c198119510aa..ad2eb15e4dd2 100644
--- a/server/etcdserver/apply/corrupt.go
+++ b/server/etcdserver/apply/corrupt.go
@@ -38,7 +38,7 @@ func (a *applierV3Corrupt) DeleteRange(_ *pb.DeleteRangeRequest) (*pb.DeleteRang
return nil, nil, errors.ErrCorrupt
}
-func (a *applierV3Corrupt) Txn(_ *pb.TxnRequest) (*pb.TxnResponse, *traceutil.Trace, error) {
+func (a *applierV3Corrupt) Txn(_ *pb.TxnRequest, _ bool) (*pb.TxnResponse, *traceutil.Trace, error) {
return nil, nil, errors.ErrCorrupt
}
diff --git a/server/etcdserver/apply/interface.go b/server/etcdserver/apply/interface.go
new file mode 100644
index 000000000000..00ba221ed922
--- /dev/null
+++ b/server/etcdserver/apply/interface.go
@@ -0,0 +1,126 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 apply
+
+import (
+ "time"
+
+ "go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/membershippb"
+ "go.etcd.io/etcd/client/pkg/v3/types"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/auth"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/membership"
+ "go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
+ "go.etcd.io/etcd/server/v3/etcdserver/cindex"
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/backend"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+// InternalRaftRequestWrapper carries per-apply metadata for an InternalRaftRequest.
+type InternalRaftRequestWrapper struct {
+ *pb.InternalRaftRequest
+ // SkipRangeExecution skips execution of range requests inside a txn for
+ // members that do not need the response; validation via checkRange still runs.
+ SkipRangeExecution bool
+}
+
+// applierV3 is the interface for processing V3 raft messages
+type applierV3 interface {
+ // Apply executes the generic portion of application logic for the current applier, but
+ // delegates the actual execution to the applyFunc method.
+ Apply(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3, applyFunc applyFunc) *Result
+
+ Put(p *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error)
+ Range(r *pb.RangeRequest) (*pb.RangeResponse, *traceutil.Trace, error)
+ DeleteRange(dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, *traceutil.Trace, error)
+ Txn(rt *pb.TxnRequest, skipRangeExecution bool) (*pb.TxnResponse, *traceutil.Trace, error)
+ Compaction(compaction *pb.CompactionRequest) (*pb.CompactionResponse, <-chan struct{}, *traceutil.Trace, error)
+
+ LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error)
+ LeaseRevoke(lc *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error)
+
+ LeaseCheckpoint(lc *pb.LeaseCheckpointRequest) (*pb.LeaseCheckpointResponse, error)
+
+ Alarm(*pb.AlarmRequest) (*pb.AlarmResponse, error)
+
+ Authenticate(r *pb.InternalAuthenticateRequest) (*pb.AuthenticateResponse, error)
+
+ AuthEnable() (*pb.AuthEnableResponse, error)
+ AuthDisable() (*pb.AuthDisableResponse, error)
+ AuthStatus() (*pb.AuthStatusResponse, error)
+
+ UserAdd(ua *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error)
+ UserDelete(ua *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error)
+ UserChangePassword(ua *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error)
+ UserGrantRole(ua *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error)
+ UserGet(ua *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error)
+ UserRevokeRole(ua *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error)
+ RoleAdd(ua *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error)
+ RoleGrantPermission(ua *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error)
+ RoleGet(ua *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error)
+ RoleRevokePermission(ua *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error)
+ RoleDelete(ua *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error)
+ UserList(ua *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error)
+ RoleList(ua *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error)
+ ClusterVersionSet(r *membershippb.ClusterVersionSetRequest, shouldApplyV3 membership.ShouldApplyV3)
+ ClusterMemberAttrSet(r *membershippb.ClusterMemberAttrSetRequest, shouldApplyV3 membership.ShouldApplyV3)
+ DowngradeInfoSet(r *membershippb.DowngradeInfoSetRequest, shouldApplyV3 membership.ShouldApplyV3)
+}
+
+type ApplierOptions struct {
+ Logger *zap.Logger
+ KV mvcc.KV
+ AlarmStore *v3alarm.AlarmStore
+ AuthStore auth.AuthStore
+ Lessor lease.Lessor
+ Cluster *membership.RaftCluster
+ RaftStatus RaftStatusGetter
+ SnapshotServer SnapshotServer
+ ConsistentIndex cindex.ConsistentIndexer
+ TxnModeWriteWithSharedBuffer bool
+ Backend backend.Backend
+ QuotaBackendBytesCfg int64
+ WarningApplyDuration time.Duration
+}
+
+type SnapshotServer interface {
+ ForceSnapshot()
+}
+
+// RaftStatusGetter represents etcd server and Raft progress.
+type RaftStatusGetter interface {
+ MemberID() types.ID
+ Leader() types.ID
+ CommittedIndex() uint64
+ AppliedIndex() uint64
+ Term() uint64
+}
+
+type Result struct {
+ Resp proto.Message
+ Err error
+ // Physc signals the physical effect of the request has completed in addition
+ // to being logically reflected by the node. Currently, only used for
+ // Compaction requests.
+ Physc <-chan struct{}
+ Trace *traceutil.Trace
+}
+
+type applyFunc func(*InternalRaftRequestWrapper, membership.ShouldApplyV3) *Result
diff --git a/server/etcdserver/apply/quota.go b/server/etcdserver/apply/quota.go
new file mode 100644
index 000000000000..f172093d906a
--- /dev/null
+++ b/server/etcdserver/apply/quota.go
@@ -0,0 +1,61 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 apply
+
+import (
+ "go.uber.org/zap"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/etcdserver/errors"
+ serverstorage "go.etcd.io/etcd/server/v3/storage"
+ "go.etcd.io/etcd/server/v3/storage/backend"
+)
+
+type quotaApplierV3 struct {
+ applierV3
+ q serverstorage.Quota
+}
+
+func newQuotaApplierV3(lg *zap.Logger, quotaBackendBytesCfg int64, be backend.Backend, app applierV3) applierV3 {
+ return "aApplierV3{app, serverstorage.NewBackendQuota(lg, quotaBackendBytesCfg, be, "v3-applier")}
+}
+
+func (a *quotaApplierV3) Put(p *pb.PutRequest) (*pb.PutResponse, *traceutil.Trace, error) {
+ ok := a.q.Available(p)
+ resp, trace, err := a.applierV3.Put(p)
+ if err == nil && !ok {
+ err = errors.ErrNoSpace
+ }
+ return resp, trace, err
+}
+
+func (a *quotaApplierV3) Txn(rt *pb.TxnRequest, skipRangeExecution bool) (*pb.TxnResponse, *traceutil.Trace, error) {
+ ok := a.q.Available(rt)
+ resp, trace, err := a.applierV3.Txn(rt, skipRangeExecution)
+ if err == nil && !ok {
+ err = errors.ErrNoSpace
+ }
+ return resp, trace, err
+}
+
+func (a *quotaApplierV3) LeaseGrant(lc *pb.LeaseGrantRequest) (*pb.LeaseGrantResponse, error) {
+ ok := a.q.Available(lc)
+ resp, err := a.applierV3.LeaseGrant(lc)
+ if err == nil && !ok {
+ err = errors.ErrNoSpace
+ }
+ return resp, err
+}
diff --git a/server/etcdserver/apply/uber_applier.go b/server/etcdserver/apply/uber_applier.go
index ec7e2aae6632..edb3bd0fefe5 100644
--- a/server/etcdserver/apply/uber_applier.go
+++ b/server/etcdserver/apply/uber_applier.go
@@ -21,18 +21,14 @@ import (
"go.uber.org/zap"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/server/v3/auth"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
- "go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/etcdserver/txn"
- "go.etcd.io/etcd/server/v3/lease"
- "go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/mvcc"
)
type UberApplier interface {
- Apply(r *pb.InternalRaftRequest) *Result
+ Apply(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3) *Result
}
type uberApplier struct {
@@ -48,27 +44,13 @@ type uberApplier struct {
applyV3base applierV3
}
-func NewUberApplier(
- lg *zap.Logger,
- be backend.Backend,
- kv mvcc.KV,
- alarmStore *v3alarm.AlarmStore,
- authStore auth.AuthStore,
- lessor lease.Lessor,
- cluster *membership.RaftCluster,
- raftStatus RaftStatusGetter,
- snapshotServer SnapshotServer,
- consistentIndex cindex.ConsistentIndexer,
- warningApplyDuration time.Duration,
- txnModeWriteWithSharedBuffer bool,
- quotaBackendBytesCfg int64,
-) UberApplier {
- applyV3base := newApplierV3(lg, be, kv, alarmStore, authStore, lessor, cluster, raftStatus, snapshotServer, consistentIndex, txnModeWriteWithSharedBuffer, quotaBackendBytesCfg)
+func NewUberApplier(opts ApplierOptions) UberApplier {
+ applyV3base := newApplierV3(opts)
ua := &uberApplier{
- lg: lg,
- alarmStore: alarmStore,
- warningApplyDuration: warningApplyDuration,
+ lg: opts.Logger,
+ alarmStore: opts.AlarmStore,
+ warningApplyDuration: opts.WarningApplyDuration,
applyV3: applyV3base,
applyV3base: applyV3base,
}
@@ -76,25 +58,12 @@ func NewUberApplier(
return ua
}
-func newApplierV3(
- lg *zap.Logger,
- be backend.Backend,
- kv mvcc.KV,
- alarmStore *v3alarm.AlarmStore,
- authStore auth.AuthStore,
- lessor lease.Lessor,
- cluster *membership.RaftCluster,
- raftStatus RaftStatusGetter,
- snapshotServer SnapshotServer,
- consistentIndex cindex.ConsistentIndexer,
- txnModeWriteWithSharedBuffer bool,
- quotaBackendBytesCfg int64,
-) applierV3 {
- applierBackend := newApplierV3Backend(lg, kv, alarmStore, authStore, lessor, cluster, raftStatus, snapshotServer, consistentIndex, txnModeWriteWithSharedBuffer)
+func newApplierV3(opts ApplierOptions) applierV3 {
+ applierBackend := newApplierV3Backend(opts)
return newAuthApplierV3(
- authStore,
- newQuotaApplierV3(lg, quotaBackendBytesCfg, be, applierBackend),
- lessor,
+ opts.AuthStore,
+ newQuotaApplierV3(opts.Logger, opts.QuotaBackendBytesCfg, opts.Backend, applierBackend),
+ opts.Lessor,
)
}
@@ -110,29 +79,54 @@ func (a *uberApplier) restoreAlarms() {
}
}
-func (a *uberApplier) Apply(r *pb.InternalRaftRequest) *Result {
+func (a *uberApplier) Apply(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3) *Result {
// We first execute chain of Apply() calls down the hierarchy:
// (i.e. CorruptApplier -> CappedApplier -> Auth -> Quota -> Backend),
// then dispatch() unpacks the request to a specific method (like Put),
// that gets executed down the hierarchy again:
// i.e. CorruptApplier.Put(CappedApplier.Put(...(BackendApplier.Put(...)))).
- return a.applyV3.Apply(r, a.dispatch)
+ return a.applyV3.Apply(r, shouldApplyV3, a.dispatch)
}
// dispatch translates the request (r) into appropriate call (like Put) on
// the underlying applyV3 object.
-func (a *uberApplier) dispatch(r *pb.InternalRaftRequest) *Result {
+func (a *uberApplier) dispatch(r *InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3) *Result {
op := "unknown"
ar := &Result{}
defer func(start time.Time) {
success := ar.Err == nil || errors.Is(ar.Err, mvcc.ErrCompacted)
- txn.ApplySecObserve(v3Version, op, success, time.Since(start))
- txn.WarnOfExpensiveRequest(a.lg, a.warningApplyDuration, start, &pb.InternalRaftStringer{Request: r}, ar.Resp, ar.Err)
+ txn.ApplySecObserve("v3", op, success, time.Since(start))
+ txn.WarnOfExpensiveRequest(a.lg, a.warningApplyDuration, start, &pb.InternalRaftStringer{Request: r.InternalRaftRequest}, ar.Resp, ar.Err)
if !success {
- txn.WarnOfFailedRequest(a.lg, start, &pb.InternalRaftStringer{Request: r}, ar.Resp, ar.Err)
+ txn.WarnOfFailedRequest(a.lg, start, &pb.InternalRaftStringer{Request: r.InternalRaftRequest}, ar.Resp, ar.Err)
}
}(time.Now())
+ switch {
+ case r.ClusterVersionSet != nil:
+ op = "ClusterVersionSet" // Implemented in 3.5.x
+ a.applyV3.ClusterVersionSet(r.ClusterVersionSet, shouldApplyV3)
+ return ar
+ case r.ClusterMemberAttrSet != nil:
+ op = "ClusterMemberAttrSet" // Implemented in 3.5.x
+ a.applyV3.ClusterMemberAttrSet(r.ClusterMemberAttrSet, shouldApplyV3)
+ return ar
+ case r.DowngradeInfoSet != nil:
+ op = "DowngradeInfoSet" // Implemented in 3.5.x
+ a.applyV3.DowngradeInfoSet(r.DowngradeInfoSet, shouldApplyV3)
+ return ar
+ case r.DowngradeVersionTest != nil:
+ op = "DowngradeVersionTest" // Implemented in 3.6 for test only
+ // do nothing, we are just to ensure etcdserver don't panic in case
+ // users(test cases) intentionally inject DowngradeVersionTestRequest
+ // into the WAL files.
+ return ar
+ default:
+ }
+ if !shouldApplyV3 {
+ return nil
+ }
+
switch {
case r.Range != nil:
op = "Range"
@@ -145,7 +139,7 @@ func (a *uberApplier) dispatch(r *pb.InternalRaftRequest) *Result {
ar.Resp, ar.Trace, ar.Err = a.applyV3.DeleteRange(r.DeleteRange)
case r.Txn != nil:
op = "Txn"
- ar.Resp, ar.Trace, ar.Err = a.applyV3.Txn(r.Txn)
+ ar.Resp, ar.Trace, ar.Err = a.applyV3.Txn(r.Txn, r.SkipRangeExecution)
case r.Compaction != nil:
op = "Compaction"
ar.Resp, ar.Physc, ar.Trace, ar.Err = a.applyV3.Compaction(r.Compaction)
diff --git a/server/etcdserver/apply/uber_applier_test.go b/server/etcdserver/apply/uber_applier_test.go
index 086db23c6cfd..e85b187721ae 100644
--- a/server/etcdserver/apply/uber_applier_test.go
+++ b/server/etcdserver/apply/uber_applier_test.go
@@ -45,6 +45,7 @@ func defaultUberApplier(t *testing.T) UberApplier {
})
cluster := membership.NewCluster(lg)
+ cluster.SetBackend(schema.NewMembershipBackend(lg, be))
cluster.AddMember(&membership.Member{ID: memberID}, true)
lessor := lease.NewLessor(lg, be, cluster, lease.LessorConfig{})
kv := mvcc.NewStore(lg, be, lessor, mvcc.StoreConfig{})
@@ -60,82 +61,83 @@ func defaultUberApplier(t *testing.T) UberApplier {
bcrypt.DefaultCost,
)
consistentIndex := cindex.NewConsistentIndex(be)
- return NewUberApplier(
- lg,
- be,
- kv,
- alarmStore,
- authStore,
- lessor,
- cluster,
- &fakeRaftStatusGetter{},
- &fakeSnapshotServer{},
- consistentIndex,
- 1*time.Hour,
- false,
- 16*1024*1024, // 16MB
- )
+ opts := ApplierOptions{
+ Logger: lg,
+ KV: kv,
+ AlarmStore: alarmStore,
+ AuthStore: authStore,
+ Lessor: lessor,
+ Cluster: cluster,
+ RaftStatus: &fakeRaftStatusGetter{},
+ SnapshotServer: &fakeSnapshotServer{},
+ ConsistentIndex: consistentIndex,
+ TxnModeWriteWithSharedBuffer: false,
+ Backend: be,
+ QuotaBackendBytesCfg: 16 * 1024 * 1024, // 16MB
+ WarningApplyDuration: time.Hour,
+ }
+ return NewUberApplier(opts)
}
// TestUberApplier_Alarm_Corrupt tests the applier returns ErrCorrupt after alarm CORRUPT is activated
func TestUberApplier_Alarm_Corrupt(t *testing.T) {
tcs := []struct {
name string
- request *pb.InternalRaftRequest
+ request *InternalRaftRequestWrapper
expectError error
}{
{
name: "Put request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{Put: &pb.PutRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Put: &pb.PutRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "Range request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "DeleteRange request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "Txn request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "Compaction request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "LeaseGrant request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}}},
expectError: errors.ErrCorrupt,
},
{
name: "LeaseRevoke request returns ErrCorrupt after alarm CORRUPT is activated",
- request: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}}},
expectError: errors.ErrCorrupt,
},
}
ua := defaultUberApplier(t)
- result := ua.Apply(&pb.InternalRaftRequest{
+ result := ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberID,
Alarm: pb.AlarmType_CORRUPT,
},
- })
+ }}, membership.ApplyBoth)
require.NotNil(t, result)
require.NoError(t, result.Err)
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- result = ua.Apply(tc.request)
+ result = ua.Apply(tc.request, membership.ApplyBoth)
require.NotNil(t, result)
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
})
@@ -146,17 +148,17 @@ func TestUberApplier_Alarm_Corrupt(t *testing.T) {
func TestUberApplier_Alarm_Quota(t *testing.T) {
tcs := []struct {
name string
- request *pb.InternalRaftRequest
+ request *InternalRaftRequestWrapper
expectError error
}{
{
name: "Put request returns ErrCorrupt after alarm NOSPACE is activated",
- request: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}}},
expectError: errors.ErrNoSpace,
},
{
name: "Txn request cost > 0 returns ErrCorrupt after alarm NOSPACE is activated",
- request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Success: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestPut{
@@ -166,12 +168,12 @@ func TestUberApplier_Alarm_Quota(t *testing.T) {
},
},
},
- }},
+ }}},
expectError: errors.ErrNoSpace,
},
{
name: "Txn request cost = 0 is still allowed after alarm NOSPACE is activated",
- request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Success: []*pb.RequestOp{
{
Request: &pb.RequestOp_RequestRange{
@@ -181,12 +183,12 @@ func TestUberApplier_Alarm_Quota(t *testing.T) {
},
},
},
- }},
+ }}},
expectError: nil,
},
{
name: "Txn request cost = 0 in both branches is still allowed after alarm NOSPACE is activated",
- request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
Compare: []*pb.Compare{
{
Key: []byte(key),
@@ -213,31 +215,31 @@ func TestUberApplier_Alarm_Quota(t *testing.T) {
},
},
},
- }},
+ }}},
expectError: nil,
},
{
name: "LeaseGrant request returns ErrCorrupt after alarm NOSPACE is activated",
- request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
+ request: &InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}}},
expectError: errors.ErrNoSpace,
},
}
ua := defaultUberApplier(t)
- result := ua.Apply(&pb.InternalRaftRequest{
+ result := ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberID,
Alarm: pb.AlarmType_NOSPACE,
},
- })
+ }}, membership.ApplyBoth)
require.NotNil(t, result)
require.NoError(t, result.Err)
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- result = ua.Apply(tc.request)
+ result = ua.Apply(tc.request, membership.ApplyBoth)
require.NotNil(t, result)
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
})
@@ -247,33 +249,33 @@ func TestUberApplier_Alarm_Quota(t *testing.T) {
// TestUberApplier_Alarm_Deactivate tests the applier should be able to apply after alarm is deactivated
func TestUberApplier_Alarm_Deactivate(t *testing.T) {
ua := defaultUberApplier(t)
- result := ua.Apply(&pb.InternalRaftRequest{
+ result := ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_ACTIVATE,
MemberID: memberID,
Alarm: pb.AlarmType_NOSPACE,
},
- })
+ }}, membership.ApplyBoth)
require.NotNil(t, result)
require.NoError(t, result.Err)
- result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}})
+ result = ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}}}, membership.ApplyBoth)
require.NotNil(t, result)
require.Equalf(t, errors.ErrNoSpace, result.Err, "Apply: got %v, expect: %v", result.Err, errors.ErrNoSpace)
- result = ua.Apply(&pb.InternalRaftRequest{
+ result = ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{
Header: &pb.RequestHeader{},
Alarm: &pb.AlarmRequest{
Action: pb.AlarmRequest_DEACTIVATE,
MemberID: memberID,
Alarm: pb.AlarmType_NOSPACE,
},
- })
+ }}, membership.ApplyBoth)
require.NotNil(t, result)
require.NoError(t, result.Err)
- result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}})
+ result = ua.Apply(&InternalRaftRequestWrapper{InternalRaftRequest: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}}}, membership.ApplyBoth)
require.NotNil(t, result)
assert.NoError(t, result.Err)
}
diff --git a/server/etcdserver/apply_v2.go b/server/etcdserver/apply_v2.go
deleted file mode 100644
index 60442fcdfde9..000000000000
--- a/server/etcdserver/apply_v2.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2016 The etcd Authors
-//
-// 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 etcdserver
-
-import (
- "encoding/json"
- "net/http"
- "path"
-
- "go.uber.org/zap"
-
- pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/api/v3/membershippb"
- "go.etcd.io/etcd/server/v3/etcdserver/api/membership"
-)
-
-func v2ToV3Request(lg *zap.Logger, r *RequestV2) pb.InternalRaftRequest {
- if r.Method != http.MethodPut || (!storeMemberAttributeRegexp.MatchString(r.Path) && r.Path != membership.StoreClusterVersionKey()) {
- lg.Panic("detected disallowed v2 WAL for stage --v2-deprecation=write-only", zap.String("method", r.Method))
- }
- if storeMemberAttributeRegexp.MatchString(r.Path) {
- id := membership.MustParseMemberIDFromKey(lg, path.Dir(r.Path))
- var attr membership.Attributes
- if err := json.Unmarshal([]byte(r.Val), &attr); err != nil {
- lg.Panic("failed to unmarshal", zap.String("value", r.Val), zap.Error(err))
- }
- return pb.InternalRaftRequest{
- Header: &pb.RequestHeader{
- ID: r.ID,
- },
- ClusterMemberAttrSet: &membershippb.ClusterMemberAttrSetRequest{
- Member_ID: uint64(id),
- MemberAttributes: &membershippb.Attributes{
- Name: attr.Name,
- ClientUrls: attr.ClientURLs,
- },
- },
- }
- }
- if r.Path == membership.StoreClusterVersionKey() {
- return pb.InternalRaftRequest{
- Header: &pb.RequestHeader{
- ID: r.ID,
- },
- ClusterVersionSet: &membershippb.ClusterVersionSetRequest{
- Ver: r.Val,
- },
- }
- }
- lg.Panic("detected disallowed v2 WAL for stage --v2-deprecation=write-only", zap.String("method", r.Method))
- return pb.InternalRaftRequest{}
-}
diff --git a/server/etcdserver/bootstrap.go b/server/etcdserver/bootstrap.go
index b76d979efea7..ae2aa9e8ae8f 100644
--- a/server/etcdserver/bootstrap.go
+++ b/server/etcdserver/bootstrap.go
@@ -27,6 +27,7 @@ import (
"github.com/coreos/go-semver/semver"
"github.com/dustin/go-humanize"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
@@ -37,8 +38,6 @@ import (
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2discovery"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3discovery"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
servererrors "go.etcd.io/etcd/server/v3/etcdserver/errors"
@@ -76,8 +75,7 @@ func bootstrap(cfg config.ServerConfig) (b *bootstrappedServer, err error) {
}
haveWAL := wal.Exist(cfg.WALDir())
- st := v2store.New(StoreClusterPrefix, StoreKeysPrefix)
- backend, err := bootstrapBackend(cfg, haveWAL, st, ss)
+ backend, err := bootstrapBackend(cfg, haveWAL)
if err != nil {
return nil, err
}
@@ -99,13 +97,27 @@ func bootstrap(cfg config.ServerConfig) (b *bootstrappedServer, err error) {
}
cfg.Logger.Info("bootstrapping storage")
- s := bootstrapStorage(cfg, st, backend, bwal, cluster)
+ s := bootstrapStorage(cfg, backend, bwal, cluster)
if err = cluster.Finalize(cfg, s); err != nil {
backend.Close()
return nil, err
}
+ if haveWAL {
+ sn := s.wal.snapshot
+ if sn == nil {
+ sn = &raftpb.Snapshot{}
+ }
+ cs := buildConfStateFromV3store(cfg.Logger, backend.be)
+
+ sn.Metadata.ConfState = &cs
+ s.wal.snapshot = sn
+
+ cfg.Logger.Info("Constructed a new raft snapshot from v3 state", zap.Uint64("index", sn.Metadata.GetIndex()),
+ zap.Uint64("term", sn.Metadata.GetTerm()), zap.String("confState", sn.Metadata.ConfState.String()))
+ }
+
cfg.Logger.Info("bootstrapping raft")
raft := bootstrapRaft(cfg, cluster, s.wal)
return &bootstrappedServer{
@@ -117,6 +129,25 @@ func bootstrap(cfg config.ServerConfig) (b *bootstrappedServer, err error) {
}, nil
}
+func buildConfStateFromV3store(lg *zap.Logger, be backend.Backend) raftpb.ConfState {
+ members, _ := schema.NewMembershipBackend(lg, be).MustReadMembersFromBackend()
+ var (
+ voters []uint64
+ learners []uint64
+ )
+ for _, m := range members {
+ if m.IsLearner {
+ learners = append(learners, uint64(m.ID))
+ } else {
+ voters = append(voters, uint64(m.ID))
+ }
+ }
+ return raftpb.ConfState{
+ Voters: voters,
+ Learners: learners,
+ }
+}
+
type bootstrappedServer struct {
storage *bootstrappedStorage
cluster *bootstrappedCluster
@@ -132,7 +163,6 @@ func (s *bootstrappedServer) Close() {
type bootstrappedStorage struct {
backend *bootstrappedBackend
wal *bootstrappedWAL
- st v2store.Store
}
func (s *bootstrappedStorage) Close() {
@@ -166,14 +196,13 @@ type bootstrappedRaft struct {
storage *raft.MemoryStorage
}
-func bootstrapStorage(cfg config.ServerConfig, st v2store.Store, be *bootstrappedBackend, wal *bootstrappedWAL, cl *bootstrappedCluster) *bootstrappedStorage {
+func bootstrapStorage(cfg config.ServerConfig, be *bootstrappedBackend, wal *bootstrappedWAL, cl *bootstrappedCluster) *bootstrappedStorage {
if wal == nil {
wal = bootstrapNewWAL(cfg, cl)
}
return &bootstrappedStorage{
backend: be,
- st: st,
wal: wal,
}
}
@@ -199,7 +228,7 @@ func bootstrapSnapshot(cfg config.ServerConfig) *snap.Snapshotter {
return snap.New(cfg.Logger, cfg.SnapDir())
}
-func bootstrapBackend(cfg config.ServerConfig, haveWAL bool, st v2store.Store, ss *snap.Snapshotter) (backend *bootstrappedBackend, err error) {
+func bootstrapBackend(cfg config.ServerConfig, haveWAL bool) (backend *bootstrappedBackend, err error) {
beExist := fileutil.Exist(cfg.BackendPath())
ci := cindex.NewConsistentIndex(nil)
beHooks := serverstorage.NewBackendHooks(cfg.Logger, ci)
@@ -222,7 +251,7 @@ func bootstrapBackend(cfg config.ServerConfig, haveWAL bool, st v2store.Store, s
// TODO(serathius): Implement schema setup in fresh storage
var snapshot *raftpb.Snapshot
if haveWAL {
- snapshot, be, err = recoverSnapshot(cfg, st, be, beExist, beHooks, ci, ss)
+ snapshot, be, err = recoverSnapshot(cfg, be, beExist, beHooks, ci)
if err != nil {
return nil, err
}
@@ -262,8 +291,8 @@ func maybeDefragBackend(cfg config.ServerConfig, be backend.Backend) error {
zap.String("current-db-size", humanize.Bytes(uint64(size))),
zap.Int64("current-db-size-in-use-bytes", sizeInUse),
zap.String("current-db-size-in-use", humanize.Bytes(uint64(sizeInUse))),
- zap.Uint("experimental-bootstrap-defrag-threshold-bytes", thresholdBytes),
- zap.String("experimental-bootstrap-defrag-threshold", humanize.Bytes(uint64(thresholdBytes))),
+ zap.Uint("bootstrap-defrag-threshold-bytes", thresholdBytes),
+ zap.String("bootstrap-defrag-threshold", humanize.Bytes(uint64(thresholdBytes))),
)
return nil
}
@@ -332,14 +361,8 @@ func bootstrapNewClusterNoWAL(cfg config.ServerConfig, prt http.RoundTripper) (*
return nil, fmt.Errorf("member %s has already been bootstrapped", m.ID)
}
if cfg.ShouldDiscover() {
- var str string
- if cfg.DiscoveryURL != "" {
- cfg.Logger.Warn("V2 discovery is deprecated!")
- str, err = v2discovery.JoinCluster(cfg.Logger, cfg.DiscoveryURL, cfg.DiscoveryProxy, m.ID, cfg.InitialPeerURLsMap.String())
- } else {
- cfg.Logger.Info("Bootstrapping cluster using v3 discovery.")
- str, err = v3discovery.JoinCluster(cfg.Logger, &cfg.DiscoveryCfg, m.ID, cfg.InitialPeerURLsMap.String())
- }
+ cfg.Logger.Info("Bootstrapping cluster using v3 discovery.")
+ str, err := v3discovery.JoinCluster(cfg.Logger, &cfg.DiscoveryCfg, m.ID, cfg.InitialPeerURLsMap.String())
if err != nil {
return nil, &servererrors.DiscoveryError{Op: "join", Err: err}
}
@@ -387,36 +410,35 @@ func bootstrapClusterWithWAL(cfg config.ServerConfig, meta *snapshotMetadata) (*
}, nil
}
-func recoverSnapshot(cfg config.ServerConfig, st v2store.Store, be backend.Backend, beExist bool, beHooks *serverstorage.BackendHooks, ci cindex.ConsistentIndexer, ss *snap.Snapshotter) (*raftpb.Snapshot, backend.Backend, error) {
+func recoverSnapshot(cfg config.ServerConfig, be backend.Backend, beExist bool, beHooks *serverstorage.BackendHooks, ci cindex.ConsistentIndexer) (*raftpb.Snapshot, backend.Backend, error) {
// Find a snapshot to start/restart a raft node
walSnaps, err := wal.ValidSnapshotEntries(cfg.Logger, cfg.WALDir())
if err != nil {
return nil, be, err
}
- // snapshot files can be orphaned if etcd crashes after writing them but before writing the corresponding
- // bwal log entries
- snapshot, err := ss.LoadNewestAvailable(walSnaps)
- if err != nil && !errors.Is(err, snap.ErrNoSnapshot) {
- return nil, be, err
- }
- if snapshot != nil {
- if err = st.Recovery(snapshot.Data); err != nil {
- cfg.Logger.Panic("failed to recover from snapshot", zap.Error(err))
+ var snapshot *raftpb.Snapshot
+ if len(walSnaps) > 0 {
+ idx := len(walSnaps) - 1
+ snapshot = &raftpb.Snapshot{
+ Metadata: &raftpb.SnapshotMetadata{
+ Term: new(walSnaps[idx].GetTerm()),
+ Index: new(walSnaps[idx].GetIndex()),
+ },
}
-
- if err = serverstorage.AssertNoV2StoreContent(cfg.Logger, st, cfg.V2Deprecation); err != nil {
- cfg.Logger.Error("illegal v2store content", zap.Error(err))
- return nil, be, err
+ if walSnaps[idx].ConfState != nil {
+ snapshot.Metadata.ConfState = proto.Clone(walSnaps[idx].ConfState).(*raftpb.ConfState)
}
-
- cfg.Logger.Info(
- "recovered v2 store from snapshot",
- zap.Uint64("snapshot-index", snapshot.Metadata.Index),
- zap.String("snapshot-size", humanize.Bytes(uint64(snapshot.Size()))),
+ cfg.Logger.Info("constructed a snapshot from WAL record",
+ zap.Uint64("snapshot-index", snapshot.Metadata.GetIndex()),
+ zap.Int("snapshot-size", proto.Size(snapshot)),
+ zap.String("confState", snapshot.Metadata.ConfState.String()),
+ zap.Int("walSnaps-count", len(walSnaps)),
)
+ }
- if be, err = serverstorage.RecoverSnapshotBackend(cfg, be, *snapshot, beExist, beHooks); err != nil {
+ if snapshot != nil {
+ if be, err = serverstorage.RecoverSnapshotBackend(cfg, be, snapshot, beExist, beHooks); err != nil {
cfg.Logger.Panic("failed to recover v3 backend from snapshot", zap.Error(err))
}
// A snapshot db may have already been recovered, and the old db should have
@@ -427,13 +449,13 @@ func recoverSnapshot(cfg config.ServerConfig, st v2store.Store, be backend.Backe
// TODO: remove kvindex != 0 checking when we do not expect users to upgrade
// etcd from pre-3.0 release.
kvindex := ci.ConsistentIndex()
- if kvindex < snapshot.Metadata.Index {
+ if kvindex < snapshot.Metadata.GetIndex() {
if kvindex != 0 {
- return nil, be, fmt.Errorf("database file (%v index %d) does not match with snapshot (index %d)", cfg.BackendPath(), kvindex, snapshot.Metadata.Index)
+ return nil, be, fmt.Errorf("database file (%v index %d) does not match with snapshot (index %d)", cfg.BackendPath(), kvindex, snapshot.Metadata.GetIndex())
}
cfg.Logger.Warn(
"consistent index was never saved",
- zap.Uint64("snapshot-index", snapshot.Metadata.Index),
+ zap.Uint64("snapshot-index", snapshot.Metadata.GetIndex()),
)
}
}
@@ -447,7 +469,6 @@ func (c *bootstrappedCluster) Finalize(cfg config.ServerConfig, s *bootstrappedS
if !s.wal.haveWAL {
c.cl.SetID(c.nodeID, c.cl.ID())
}
- c.cl.SetStore(s.st)
c.cl.SetBackend(schema.NewMembershipBackend(cfg.Logger, s.backend.be))
// Workaround the issues which have already been affected
@@ -571,7 +592,7 @@ func bootstrapWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot
if cfg.ForceNewCluster {
consistentIndex := ci.ConsistentIndex()
- oldCommitIndex := bwal.st.Commit
+ oldCommitIndex := bwal.st.GetCommit()
// If only `HardState.Commit` increases, HardState won't be persisted
// to disk, even though the committed entries might have already been
// applied. This can result in consistent_index > CommitIndex.
@@ -581,7 +602,7 @@ func bootstrapWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot
// committed, we reset Commit to max(HardState.Commit, consistent_index).
//
// See: https://github.com/etcd-io/raft/pull/300 for more details.
- bwal.st.Commit = max(oldCommitIndex, consistentIndex)
+ bwal.st.Commit = new(max(oldCommitIndex, consistentIndex))
// discard the previously uncommitted entries
bwal.ents = bwal.CommitedEntries()
@@ -593,14 +614,14 @@ func bootstrapWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot
zap.String("cluster-id", meta.clusterID.String()),
zap.String("local-member-id", meta.nodeID.String()),
zap.Uint64("wal-commit-index", oldCommitIndex),
- zap.Uint64("commit-index", bwal.st.Commit),
+ zap.Uint64("commit-index", bwal.st.GetCommit()),
)
} else {
cfg.Logger.Info(
"restarting local member",
zap.String("cluster-id", meta.clusterID.String()),
zap.String("local-member-id", meta.nodeID.String()),
- zap.Uint64("commit-index", bwal.st.Commit),
+ zap.Uint64("commit-index", bwal.st.GetCommit()),
)
}
return bwal
@@ -609,14 +630,14 @@ func bootstrapWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot
// openWALFromSnapshot reads the WAL at the given snap and returns the wal, its latest HardState and cluster ID, and all entries that appear
// after the position of the given snap in the WAL.
// The snap must have been previously saved to the WAL, or this call will panic.
-func openWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot) (*wal.WAL, *raftpb.HardState, []raftpb.Entry, *raftpb.Snapshot, *snapshotMetadata) {
+func openWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot) (*wal.WAL, *raftpb.HardState, []*raftpb.Entry, *raftpb.Snapshot, *snapshotMetadata) {
var walsnap walpb.Snapshot
if snapshot != nil {
- walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
+ walsnap.Index, walsnap.Term = new(snapshot.Metadata.GetIndex()), new(snapshot.Metadata.GetTerm())
}
repaired := false
for {
- w, err := wal.Open(cfg.Logger, cfg.WALDir(), walsnap)
+ w, err := wal.Open(cfg.Logger, cfg.WALDir(), &walsnap)
if err != nil {
cfg.Logger.Fatal("failed to open WAL", zap.Error(err))
}
@@ -639,11 +660,11 @@ func openWALFromSnapshot(cfg config.ServerConfig, snapshot *raftpb.Snapshot) (*w
continue
}
var metadata etcdserverpb.Metadata
- pbutil.MustUnmarshal(&metadata, wmetadata)
- id := types.ID(metadata.NodeID)
- cid := types.ID(metadata.ClusterID)
+ pbutil.MustUnmarshalMessage(&metadata, wmetadata)
+ id := types.ID(metadata.GetNodeID())
+ cid := types.ID(metadata.GetClusterID())
meta := &snapshotMetadata{clusterID: cid, nodeID: id}
- return w, &st, ents, snapshot, meta
+ return w, st, ents, snapshot, meta
}
}
@@ -652,10 +673,10 @@ type snapshotMetadata struct {
}
func bootstrapNewWAL(cfg config.ServerConfig, cl *bootstrappedCluster) *bootstrappedWAL {
- metadata := pbutil.MustMarshal(
+ metadata := pbutil.MustMarshalMessage(
&etcdserverpb.Metadata{
- NodeID: uint64(cl.nodeID),
- ClusterID: uint64(cl.cl.ID()),
+ NodeID: new(uint64(cl.nodeID)),
+ ClusterID: new(uint64(cl.cl.ID())),
},
)
w, err := wal.Create(cfg.Logger, cfg.WALDir(), metadata)
@@ -677,7 +698,7 @@ type bootstrappedWAL struct {
haveWAL bool
w *wal.WAL
st *raftpb.HardState
- ents []raftpb.Entry
+ ents []*raftpb.Entry
snapshot *raftpb.Snapshot
meta *snapshotMetadata
}
@@ -685,10 +706,10 @@ type bootstrappedWAL struct {
func (wal *bootstrappedWAL) MemoryStorage() *raft.MemoryStorage {
s := raft.NewMemoryStorage()
if wal.snapshot != nil {
- s.ApplySnapshot(*wal.snapshot)
+ s.ApplySnapshot(wal.snapshot)
}
if wal.st != nil {
- s.SetHardState(*wal.st)
+ s.SetHardState(wal.st)
}
if len(wal.ents) != 0 {
s.Append(wal.ents)
@@ -696,13 +717,13 @@ func (wal *bootstrappedWAL) MemoryStorage() *raft.MemoryStorage {
return s
}
-func (wal *bootstrappedWAL) CommitedEntries() []raftpb.Entry {
+func (wal *bootstrappedWAL) CommitedEntries() []*raftpb.Entry {
for i, ent := range wal.ents {
- if ent.Index > wal.st.Commit {
+ if ent.GetIndex() > wal.st.GetCommit() {
wal.lg.Info(
"discarding uncommitted WAL entries",
- zap.Uint64("entry-index", ent.Index),
- zap.Uint64("commit-index-from-wal", wal.st.Commit),
+ zap.Uint64("entry-index", ent.GetIndex()),
+ zap.Uint64("commit-index-from-wal", wal.st.GetCommit()),
zap.Int("number-of-discarded-entries", len(wal.ents)-i),
)
return wal.ents[:i]
@@ -711,19 +732,19 @@ func (wal *bootstrappedWAL) CommitedEntries() []raftpb.Entry {
return wal.ents
}
-func (wal *bootstrappedWAL) NewConfigChangeEntries() []raftpb.Entry {
+func (wal *bootstrappedWAL) NewConfigChangeEntries() []*raftpb.Entry {
return serverstorage.CreateConfigChangeEnts(
wal.lg,
serverstorage.GetEffectiveNodeIDsFromWALEntries(wal.lg, wal.snapshot, wal.ents),
uint64(wal.meta.nodeID),
- wal.st.Term,
- wal.st.Commit,
+ wal.st.GetTerm(),
+ wal.st.GetCommit(),
)
}
-func (wal *bootstrappedWAL) AppendAndCommitEntries(ents []raftpb.Entry) {
+func (wal *bootstrappedWAL) AppendAndCommitEntries(ents []*raftpb.Entry) {
wal.ents = append(wal.ents, ents...)
- err := wal.w.Save(raftpb.HardState{}, ents)
+ err := wal.w.Save(&raftpb.HardState{}, ents)
if err != nil {
wal.lg.Fatal("failed to save hard state and entries", zap.Error(err))
}
diff --git a/server/etcdserver/bootstrap_test.go b/server/etcdserver/bootstrap_test.go
index ca16c5887b6b..f0dfb4e08ff4 100644
--- a/server/etcdserver/bootstrap_test.go
+++ b/server/etcdserver/bootstrap_test.go
@@ -37,7 +37,6 @@ import (
"go.etcd.io/etcd/server/v3/config"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
serverstorage "go.etcd.io/etcd/server/v3/storage"
"go.etcd.io/etcd/server/v3/storage/datadir"
"go.etcd.io/etcd/server/v3/storage/schema"
@@ -133,7 +132,7 @@ func mockBootstrapRoundTrip(members []etcdserverpb.Member) roundTripFunc {
case strings.Contains(r.URL.String(), DowngradeEnabledPath):
return &http.Response{
StatusCode: http.StatusOK,
- Body: io.NopCloser(strings.NewReader(`true`)),
+ Body: io.NopCloser(strings.NewReader(`false`)),
}, nil
}
return nil, nil
@@ -191,9 +190,7 @@ func TestBootstrapBackend(t *testing.T) {
}
haveWAL := wal.Exist(cfg.WALDir())
- st := v2store.New(StoreClusterPrefix, StoreKeysPrefix)
- ss := snap.New(cfg.Logger, cfg.SnapDir())
- backend, err := bootstrapBackend(cfg, haveWAL, st, ss)
+ backend, err := bootstrapBackend(cfg, haveWAL)
defer t.Cleanup(func() {
backend.Close()
})
@@ -256,19 +253,19 @@ func createWALFileWithSnapshotRecord(cfg config.ServerConfig, snapshotTerm, snap
}()
walSnap := walpb.Snapshot{
- Index: snapshotIndex,
- Term: snapshotTerm,
+ Index: &snapshotIndex,
+ Term: &snapshotTerm,
ConfState: &raftpb.ConfState{
Voters: []uint64{0x00ffca74},
- AutoLeave: false,
+ AutoLeave: new(false),
},
}
- if err = w.SaveSnapshot(walSnap); err != nil {
+ if err = w.SaveSnapshot(&walSnap); err != nil {
return err
}
- return w.Save(raftpb.HardState{Term: snapshotTerm, Vote: 3, Commit: snapshotIndex}, nil)
+ return w.Save(&raftpb.HardState{Term: &snapshotTerm, Vote: new(uint64(3)), Commit: &snapshotIndex}, nil)
}
func createSnapshotAndBackendDB(cfg config.ServerConfig, snapshotTerm, snapshotIndex uint64) error {
@@ -280,12 +277,12 @@ func createSnapshotAndBackendDB(cfg config.ServerConfig, snapshotTerm, snapshotI
// create snapshot file
ss := snap.New(cfg.Logger, cfg.SnapDir())
- if err = ss.SaveSnap(raftpb.Snapshot{
+ if err = ss.SaveSnap(&raftpb.Snapshot{
Data: []byte("{}"),
- Metadata: raftpb.SnapshotMetadata{
- ConfState: confState,
- Index: snapshotIndex,
- Term: snapshotTerm,
+ Metadata: &raftpb.SnapshotMetadata{
+ ConfState: &confState,
+ Index: &snapshotIndex,
+ Term: &snapshotTerm,
},
}); err != nil {
return err
diff --git a/server/etcdserver/cluster_util_test.go b/server/etcdserver/cluster_util_test.go
index 1ca9ec1345f8..d3f65364465e 100644
--- a/server/etcdserver/cluster_util_test.go
+++ b/server/etcdserver/cluster_util_test.go
@@ -17,11 +17,11 @@ package etcdserver
import (
"testing"
- "go.etcd.io/etcd/api/v3/version"
- "go.etcd.io/etcd/client/pkg/v3/types"
-
"github.com/coreos/go-semver/semver"
"go.uber.org/zap/zaptest"
+
+ "go.etcd.io/etcd/api/v3/version"
+ "go.etcd.io/etcd/client/pkg/v3/types"
)
func TestIsCompatibleWithVers(t *testing.T) {
diff --git a/server/etcdserver/corrupt.go b/server/etcdserver/corrupt.go
index 5ec111bef43f..0dc563e2f670 100644
--- a/server/etcdserver/corrupt.go
+++ b/server/etcdserver/corrupt.go
@@ -438,7 +438,7 @@ func (s *EtcdServer) triggerCorruptAlarm(id types.ID) {
Alarm: pb.AlarmType_CORRUPT,
}
s.GoAttach(func() {
- s.raftRequest(s.ctx, pb.InternalRaftRequest{Alarm: a})
+ s.raftRequest(s.ctx, &pb.InternalRaftRequest{Alarm: a})
})
}
@@ -590,7 +590,7 @@ func HashByRev(ctx context.Context, cid types.ID, cc *http.Client, url string, r
req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Etcd-Cluster-ID", cid.String())
- req.Cancel = ctx.Done()
+ req.Cancel = ctx.Done() //nolint:staticcheck // TODO: remove for a supported version
resp, err := cc.Do(req)
if err != nil {
diff --git a/server/etcdserver/errors/errors.go b/server/etcdserver/errors/errors.go
index 8de698a1df34..4fb78ba83697 100644
--- a/server/etcdserver/errors/errors.go
+++ b/server/etcdserver/errors/errors.go
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+//revive:disable-next-line:var-naming
package errors
import (
diff --git a/server/etcdserver/interface.go b/server/etcdserver/interface.go
new file mode 100644
index 000000000000..aff17a32490b
--- /dev/null
+++ b/server/etcdserver/interface.go
@@ -0,0 +1,48 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 etcdserver
+
+import (
+ "context"
+ "time"
+)
+
+func (s *EtcdServer) ApplyWait(deadline uint64) <-chan struct{} {
+ return s.applyWait.Wait(deadline)
+}
+
+func (s *EtcdServer) Done() <-chan struct{} {
+ return s.done
+}
+
+func (s *EtcdServer) LeaderChanged() <-chan struct{} {
+ return s.leaderChanged.Receive()
+}
+
+func (s *EtcdServer) NextRequestID() uint64 {
+ return s.reqIDGen.Next()
+}
+
+func (s *EtcdServer) RequestTimeout() time.Duration {
+ return s.Cfg.ReqTimeout()
+}
+
+func (s *EtcdServer) Stopping() <-chan struct{} {
+ return s.stopping
+}
+
+func (s *EtcdServer) LinearizableReadNotify(ctx context.Context) error {
+ return s.read.LinearizableReadNotify(ctx)
+}
diff --git a/server/etcdserver/metrics.go b/server/etcdserver/metrics.go
index 7176d30adbc0..06edaab5c2bf 100644
--- a/server/etcdserver/metrics.go
+++ b/server/etcdserver/metrics.go
@@ -95,18 +95,19 @@ var (
Name: "proposals_failed_total",
Help: "The total number of failed proposals seen.",
})
- slowReadIndex = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "etcd",
- Subsystem: "server",
- Name: "slow_read_indexes_total",
- Help: "The total number of pending read indexes not in sync with leader's or timed out read index requests.",
- })
- readIndexFailed = prometheus.NewCounter(prometheus.CounterOpts{
- Namespace: "etcd",
- Subsystem: "server",
- Name: "read_indexes_failed_total",
- Help: "The total number of failed read indexes seen.",
- })
+ requestDurationSec = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "request_duration_seconds",
+ Help: "Response latency distribution in seconds for each type.",
+
+ // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2
+ // highest bucket start of 0.001 sec * 2^13 == 8.192 sec
+ Buckets: prometheus.ExponentialBuckets(0.001, 2, 14),
+ },
+ []string{"type", "success"},
+ )
leaseExpired = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: "etcd_debugging",
Subsystem: "server",
@@ -171,8 +172,7 @@ func init() {
prometheus.MustRegister(proposalsApplied)
prometheus.MustRegister(proposalsPending)
prometheus.MustRegister(proposalsFailed)
- prometheus.MustRegister(slowReadIndex)
- prometheus.MustRegister(readIndexFailed)
+ prometheus.MustRegister(requestDurationSec)
prometheus.MustRegister(leaseExpired)
prometheus.MustRegister(currentVersion)
prometheus.MustRegister(currentGoVersion)
diff --git a/server/etcdserver/raft.go b/server/etcdserver/raft.go
index fd4b5dac3371..c60bd2d21d6d 100644
--- a/server/etcdserver/raft.go
+++ b/server/etcdserver/raft.go
@@ -22,6 +22,7 @@ import (
"time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/client/pkg/v3/logutil"
"go.etcd.io/etcd/pkg/v3/contention"
@@ -67,8 +68,8 @@ func init() {
// raft storage concurrently; the application must read
// notifyc before assuming the raft messages are stable.
type toApply struct {
- entries []raftpb.Entry
- snapshot raftpb.Snapshot
+ entries []*raftpb.Entry
+ snapshot *raftpb.Snapshot
// notifyc synchronizes etcd server applies with the raft node
notifyc chan struct{}
// raftAdvancedC notifies EtcdServer.apply that
@@ -86,7 +87,7 @@ type raftNode struct {
raftNodeConfig
// a chan to send/receive snapshot
- msgSnapC chan raftpb.Message
+ msgSnapC chan *raftpb.Message
// a chan to send out apply
applyc chan toApply
@@ -141,7 +142,7 @@ func newRaftNode(cfg raftNodeConfig) *raftNode {
// expect to send a heartbeat within 2 heartbeat intervals.
td: contention.NewTimeoutDetector(2 * cfg.heartbeat),
readStateC: make(chan raft.ReadState, 1),
- msgSnapC: make(chan raftpb.Message, maxInFlightMsgSnap),
+ msgSnapC: make(chan *raftpb.Message, maxInFlightMsgSnap),
applyc: make(chan toApply),
stopped: make(chan struct{}),
done: make(chan struct{}),
@@ -214,12 +215,13 @@ func (r *raftNode) start(rh *raftReadyHandler) {
return
}
}
-
+ committedEntries := rd.CommittedEntries
notifyc := make(chan struct{}, 1)
raftAdvancedC := make(chan struct{}, 1)
+ raftSnap := proto.Clone(rd.Snapshot).(*raftpb.Snapshot)
ap := toApply{
- entries: rd.CommittedEntries,
- snapshot: rd.Snapshot,
+ entries: committedEntries,
+ snapshot: proto.Clone(rd.Snapshot).(*raftpb.Snapshot),
notifyc: notifyc,
raftAdvancedC: raftAdvancedC,
}
@@ -242,9 +244,9 @@ func (r *raftNode) start(rh *raftReadyHandler) {
// Must save the snapshot file and WAL snapshot entry before saving any other entries or hardstate to
// ensure that recovery after a snapshot restore is possible.
- if !raft.IsEmptySnap(rd.Snapshot) {
+ if !raft.IsEmptySnap(raftSnap) {
// gofail: var raftBeforeSaveSnap struct{}
- if err := r.storage.SaveSnap(rd.Snapshot); err != nil {
+ if err := r.storage.SaveSnap(raftSnap); err != nil {
r.lg.Fatal("failed to save Raft snapshot", zap.Error(err))
}
// gofail: var raftAfterSaveSnap struct{}
@@ -255,11 +257,11 @@ func (r *raftNode) start(rh *raftReadyHandler) {
r.lg.Fatal("failed to save Raft hard state and entries", zap.Error(err))
}
if !raft.IsEmptyHardState(rd.HardState) {
- proposalsCommitted.Set(float64(rd.HardState.Commit))
+ proposalsCommitted.Set(float64(rd.HardState.GetCommit()))
}
// gofail: var raftAfterSave struct{}
- if !raft.IsEmptySnap(rd.Snapshot) {
+ if !raft.IsEmptySnap(raftSnap) {
// Force WAL to fsync its hard state before Release() releases
// old data from the WAL. Otherwise could get an error like:
// panic: tocommit(107) is out of range [lastIndex(84)]. Was the raft log corrupted, truncated, or lost?
@@ -272,11 +274,11 @@ func (r *raftNode) start(rh *raftReadyHandler) {
notifyc <- struct{}{}
// gofail: var raftBeforeApplySnap struct{}
- r.raftStorage.ApplySnapshot(rd.Snapshot)
- r.lg.Info("applied incoming Raft snapshot", zap.Uint64("snapshot-index", rd.Snapshot.Metadata.Index))
+ r.raftStorage.ApplySnapshot(raftSnap)
+ r.lg.Info("applied incoming Raft snapshot", zap.Uint64("snapshot-index", raftSnap.Metadata.GetIndex()))
// gofail: var raftAfterApplySnap struct{}
- if err := r.storage.Release(rd.Snapshot); err != nil {
+ if err := r.storage.Release(raftSnap); err != nil {
r.lg.Fatal("failed to release Raft wal", zap.Error(err))
}
// gofail: var raftAfterWALRelease struct{}
@@ -286,7 +288,7 @@ func (r *raftNode) start(rh *raftReadyHandler) {
confChanged := false
for _, ent := range rd.CommittedEntries {
- if ent.Type == raftpb.EntryConfChange {
+ if ent.GetType() == raftpb.EntryConfChange {
confChanged = true
break
}
@@ -342,51 +344,51 @@ func (r *raftNode) start(rh *raftReadyHandler) {
func updateCommittedIndex(ap *toApply, rh *raftReadyHandler) {
var ci uint64
if len(ap.entries) != 0 {
- ci = ap.entries[len(ap.entries)-1].Index
+ ci = ap.entries[len(ap.entries)-1].GetIndex()
}
- if ap.snapshot.Metadata.Index > ci {
- ci = ap.snapshot.Metadata.Index
+ if ap.snapshot != nil && ap.snapshot.Metadata.GetIndex() > ci {
+ ci = ap.snapshot.Metadata.GetIndex()
}
if ci != 0 {
rh.updateCommittedIndex(ci)
}
}
-func (r *raftNode) processMessages(ms []raftpb.Message) []raftpb.Message {
+func (r *raftNode) processMessages(ms []*raftpb.Message) []*raftpb.Message {
sentAppResp := false
+ var messages []*raftpb.Message
for i := len(ms) - 1; i >= 0; i-- {
- if r.isIDRemoved(ms[i].To) {
- ms[i].To = 0
+ m := ms[i]
+ if r.isIDRemoved(m.GetTo()) {
continue
}
- if ms[i].Type == raftpb.MsgAppResp {
+ if m.GetType() == raftpb.MsgAppResp {
if sentAppResp {
- ms[i].To = 0
- } else {
- sentAppResp = true
+ continue
}
+ sentAppResp = true
}
- if ms[i].Type == raftpb.MsgSnap {
+ if m.GetType() == raftpb.MsgSnap {
// There are two separate data store: the store for v2, and the KV for v3.
// The msgSnap only contains the most recent snapshot of store without KV.
// So we need to redirect the msgSnap to etcd server main loop for merging in the
// current store snapshot and KV snapshot.
select {
- case r.msgSnapC <- ms[i]:
+ case r.msgSnapC <- m:
default:
// drop msgSnap if the inflight chan if full.
}
- ms[i].To = 0
+ continue
}
- if ms[i].Type == raftpb.MsgHeartbeat {
- ok, exceed := r.td.Observe(ms[i].To)
+ if m.GetType() == raftpb.MsgHeartbeat {
+ ok, exceed := r.td.Observe(m.GetTo())
if !ok {
// TODO: limit request rate.
r.lg.Warn(
"leader failed to send out heartbeat on time; took too long, leader is overloaded likely from slow disk",
- zap.String("to", fmt.Sprintf("%x", ms[i].To)),
+ zap.String("to", fmt.Sprintf("%x", m.GetTo())),
zap.Duration("heartbeat-interval", r.heartbeat),
zap.Duration("expected-duration", 2*r.heartbeat),
zap.Duration("exceeded-duration", exceed),
@@ -394,8 +396,9 @@ func (r *raftNode) processMessages(ms []raftpb.Message) []raftpb.Message {
heartbeatSendFailures.Inc()
}
}
+ messages = append(messages, m)
}
- return ms
+ return messages
}
func (r *raftNode) apply() chan toApply {
@@ -444,3 +447,7 @@ func (r *raftNode) advanceTicks(ticks int) {
r.tick()
}
}
+
+func (r *raftNode) ReadState() <-chan raft.ReadState {
+ return r.readStateC
+}
diff --git a/server/etcdserver/raft_test.go b/server/etcdserver/raft_test.go
index 2cfa4816232b..bb2763ab473c 100644
--- a/server/etcdserver/raft_test.go
+++ b/server/etcdserver/raft_test.go
@@ -22,7 +22,10 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/pbutil"
@@ -35,49 +38,49 @@ import (
func TestGetIDs(t *testing.T) {
lg := zaptest.NewLogger(t)
- addcc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 2}
- addEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc)}
- removecc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
- removeEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc)}
- normalEntry := raftpb.Entry{Type: raftpb.EntryNormal}
- updatecc := &raftpb.ConfChange{Type: raftpb.ConfChangeUpdateNode, NodeID: 2}
- updateEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(updatecc)}
+ addcc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode.Enum(), NodeId: new(uint64(2))}
+ addEntry := &raftpb.Entry{Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(addcc)}
+ removecc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode.Enum(), NodeId: new(uint64(2))}
+ removeEntry := &raftpb.Entry{Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc)}
+ normalEntry := &raftpb.Entry{Type: raftpb.EntryNormal.Enum()}
+ updatecc := &raftpb.ConfChange{Type: raftpb.ConfChangeUpdateNode.Enum(), NodeId: new(uint64(2))}
+ updateEntry := &raftpb.Entry{Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(updatecc)}
tests := []struct {
confState *raftpb.ConfState
- ents []raftpb.Entry
+ ents []*raftpb.Entry
widSet []uint64
}{
- {nil, []raftpb.Entry{}, []uint64{}},
+ {nil, []*raftpb.Entry{}, []uint64{}},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{},
+ []*raftpb.Entry{},
[]uint64{1},
},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{addEntry},
+ []*raftpb.Entry{addEntry},
[]uint64{1, 2},
},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{addEntry, removeEntry},
+ []*raftpb.Entry{addEntry, removeEntry},
[]uint64{1},
},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{addEntry, normalEntry},
+ []*raftpb.Entry{addEntry, normalEntry},
[]uint64{1, 2},
},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{addEntry, normalEntry, updateEntry},
+ []*raftpb.Entry{addEntry, normalEntry, updateEntry},
[]uint64{1, 2},
},
{
&raftpb.ConfState{Voters: []uint64{1}},
- []raftpb.Entry{addEntry, removeEntry, normalEntry},
+ []*raftpb.Entry{addEntry, removeEntry, normalEntry},
[]uint64{1},
},
}
@@ -85,7 +88,7 @@ func TestGetIDs(t *testing.T) {
for i, tt := range tests {
var snap raftpb.Snapshot
if tt.confState != nil {
- snap.Metadata.ConfState = *tt.confState
+ snap.Metadata = &raftpb.SnapshotMetadata{ConfState: tt.confState}
}
idSet := serverstorage.GetEffectiveNodeIDsFromWALEntries(lg, &snap, tt.ents)
if !reflect.DeepEqual(idSet, tt.widSet) {
@@ -104,15 +107,15 @@ func TestCreateConfigChangeEnts(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- addcc1 := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1, Context: ctx}
- removecc2 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
- removecc3 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 3}
+ addcc1 := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode.Enum(), NodeId: new(uint64(1)), Context: ctx}
+ removecc2 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode.Enum(), NodeId: new(uint64(2))}
+ removecc3 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode.Enum(), NodeId: new(uint64(3))}
tests := []struct {
ids []uint64
self uint64
term, index uint64
- wents []raftpb.Entry
+ wents []*raftpb.Entry
}{
{
[]uint64{1},
@@ -126,23 +129,23 @@ func TestCreateConfigChangeEnts(t *testing.T) {
1,
1, 1,
- []raftpb.Entry{{Term: 1, Index: 2, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
+ []*raftpb.Entry{{Term: new(uint64(1)), Index: new(uint64(2)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc2)}},
},
{
[]uint64{1, 2},
1,
2, 2,
- []raftpb.Entry{{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
+ []*raftpb.Entry{{Term: new(uint64(2)), Index: new(uint64(3)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc2)}},
},
{
[]uint64{1, 2, 3},
1,
2, 2,
- []raftpb.Entry{
- {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
- {Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ []*raftpb.Entry{
+ {Term: new(uint64(2)), Index: new(uint64(3)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc2)},
+ {Term: new(uint64(2)), Index: new(uint64(4)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc3)},
},
},
{
@@ -150,8 +153,8 @@ func TestCreateConfigChangeEnts(t *testing.T) {
2,
2, 2,
- []raftpb.Entry{
- {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ []*raftpb.Entry{
+ {Term: new(uint64(2)), Index: new(uint64(3)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc3)},
},
},
{
@@ -159,18 +162,18 @@ func TestCreateConfigChangeEnts(t *testing.T) {
1,
2, 2,
- []raftpb.Entry{
- {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc1)},
- {Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
- {Term: 2, Index: 5, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ []*raftpb.Entry{
+ {Term: new(uint64(2)), Index: new(uint64(3)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(addcc1)},
+ {Term: new(uint64(2)), Index: new(uint64(4)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc2)},
+ {Term: new(uint64(2)), Index: new(uint64(5)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(removecc3)},
},
},
}
for i, tt := range tests {
gents := serverstorage.CreateConfigChangeEnts(lg, tt.ids, tt.self, tt.term, tt.index)
- if !reflect.DeepEqual(gents, tt.wents) {
- t.Errorf("#%d: ents = %v, want %v", i, gents, tt.wents)
+ if diff := cmp.Diff(tt.wents, gents, protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" {
+ t.Errorf("#%d: unexpected entries (-want +got):\n%s", i, diff)
}
}
}
@@ -229,7 +232,7 @@ func TestConfigChangeBlocksApply(t *testing.T) {
n.readyc <- raft.Ready{
SoftState: &raft.SoftState{RaftState: raft.StateFollower},
- CommittedEntries: []raftpb.Entry{{Type: raftpb.EntryConfChange}},
+ CommittedEntries: []*raftpb.Entry{{Type: raftpb.EntryConfChange.Enum()}},
}
ap := <-srv.r.applyc
@@ -251,7 +254,7 @@ func TestConfigChangeBlocksApply(t *testing.T) {
select {
case <-ap.raftAdvancedC:
- t.Log("recevied raft advance notification")
+ t.Log("received raft advance notification")
}
select {
@@ -278,11 +281,10 @@ func TestProcessDuplicatedAppRespMessage(t *testing.T) {
})
s := &EtcdServer{
- lgMu: new(sync.RWMutex),
- lg: zaptest.NewLogger(t),
- r: *r,
- cluster: cl,
- SyncTicker: &time.Ticker{},
+ lgMu: new(sync.RWMutex),
+ lg: zaptest.NewLogger(t),
+ r: *r,
+ cluster: cl,
}
s.start()
@@ -290,10 +292,10 @@ func TestProcessDuplicatedAppRespMessage(t *testing.T) {
lead := uint64(1)
- n.readyc <- raft.Ready{Messages: []raftpb.Message{
- {Type: raftpb.MsgAppResp, From: 2, To: lead, Term: 1, Index: 1},
- {Type: raftpb.MsgAppResp, From: 2, To: lead, Term: 1, Index: 2},
- {Type: raftpb.MsgAppResp, From: 2, To: lead, Term: 1, Index: 3},
+ n.readyc <- raft.Ready{Messages: []*raftpb.Message{
+ {Type: raftpb.MsgAppResp.Enum(), From: new(uint64(2)), To: &lead, Term: new(uint64(1)), Index: new(uint64(1))},
+ {Type: raftpb.MsgAppResp.Enum(), From: new(uint64(2)), To: &lead, Term: new(uint64(1)), Index: new(uint64(2))},
+ {Type: raftpb.MsgAppResp.Enum(), From: new(uint64(2)), To: &lead, Term: new(uint64(1)), Index: new(uint64(3))},
}}
got, want := <-sendc, 1
diff --git a/server/etcdserver/read/metrics.go b/server/etcdserver/read/metrics.go
new file mode 100644
index 000000000000..9199894a3bec
--- /dev/null
+++ b/server/etcdserver/read/metrics.go
@@ -0,0 +1,39 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 read
+
+import (
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+ slowReadIndex = prometheus.NewCounter(prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "slow_read_indexes_total",
+ Help: "The total number of pending read indexes not in sync with leader's or timed out read index requests.",
+ })
+ readIndexFailed = prometheus.NewCounter(prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "read_indexes_failed_total",
+ Help: "The total number of failed read indexes seen.",
+ })
+)
+
+func init() {
+ prometheus.MustRegister(slowReadIndex)
+ prometheus.MustRegister(readIndexFailed)
+}
diff --git a/server/etcdserver/read/read.go b/server/etcdserver/read/read.go
new file mode 100644
index 000000000000..ca43132b7d94
--- /dev/null
+++ b/server/etcdserver/read/read.go
@@ -0,0 +1,258 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 read
+
+import (
+ "context"
+ "encoding/binary"
+ errorspkg "errors"
+ "strconv"
+ "sync"
+ "time"
+
+ "go.uber.org/zap"
+
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/etcdserver/errors"
+ "go.etcd.io/raft/v3"
+)
+
+var (
+ readIndexRetryTime = 500 * time.Millisecond
+)
+
+func NewRead(server server, raft raftInterface) *Read {
+ return &Read{
+ server: server,
+ raft: raft,
+ waitC: make(chan struct{}, 1),
+ notifier: newNotifier(),
+ }
+}
+
+type Read struct {
+ server server
+ raft raftInterface
+ mux sync.RWMutex
+ // read routine notifies etcd server that it waits for reading by sending an empty struct to
+ // waitC
+ waitC chan struct{}
+ // notifier is used to notify the read routine that it can process the request
+ // when there is no error
+ notifier *notifier
+}
+
+type server interface {
+ LeaderChanged() <-chan struct{}
+ Stopping() <-chan struct{}
+ Logger() *zap.Logger
+ AppliedIndex() uint64
+ ApplyWait(deadline uint64) <-chan struct{}
+ NextRequestID() uint64
+ RequestTimeout() time.Duration
+ FirstCommitInTermNotify() <-chan struct{}
+ Done() <-chan struct{}
+}
+
+type raftInterface interface {
+ ReadState() <-chan raft.ReadState
+ ReadIndex(ctx context.Context, rctx []byte) error
+}
+
+func (r *Read) LinearizableReadNotify(ctx context.Context) error {
+ r.mux.RLock()
+ nc := r.notifier
+ r.mux.RUnlock()
+
+ // signal linearizable loop for current notify if it hasn't been already
+ select {
+ case r.waitC <- struct{}{}:
+ default:
+ }
+
+ // wait for read state notification
+ select {
+ case <-nc.c:
+ return nc.err
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-r.server.Done():
+ return errors.ErrStopped
+ }
+}
+
+func (r *Read) LinearizableReadLoop() {
+ for {
+ leaderChangedNotifier := r.server.LeaderChanged()
+ select {
+ case <-leaderChangedNotifier:
+ continue
+ case <-r.waitC:
+ case <-r.server.Stopping():
+ return
+ }
+
+ // as a single loop is can unlock multiple reads, it is not very useful
+ // to propagate the trace from Txn or Range.
+ _, trace := traceutil.EnsureTrace(context.Background(), r.server.Logger(), "linearizableReadLoop")
+
+ nextnr := newNotifier()
+ r.mux.Lock()
+ nr := r.notifier
+ r.notifier = nextnr
+ r.mux.Unlock()
+
+ confirmedIndex, err := r.requestCurrentIndex(leaderChangedNotifier)
+ if isStopped(err) {
+ return
+ }
+ if err != nil {
+ nr.notify(err)
+ continue
+ }
+
+ trace.Step("read index received")
+
+ trace.AddField(traceutil.Field{Key: "readStateIndex", Value: confirmedIndex})
+
+ appliedIndex := r.server.AppliedIndex()
+ trace.AddField(traceutil.Field{Key: "appliedIndex", Value: strconv.FormatUint(appliedIndex, 10)})
+
+ if appliedIndex < confirmedIndex {
+ select {
+ case <-r.server.ApplyWait(confirmedIndex):
+ case <-r.server.Stopping():
+ return
+ }
+ }
+ // unblock all l-reads requested at indices before confirmedIndex
+ nr.notify(nil)
+ trace.Step("applied index is now lower than readState.Index")
+
+ trace.LogAllStepsIfLong(100 * time.Millisecond)
+ }
+}
+
+func (r *Read) requestCurrentIndex(leaderChangedNotifier <-chan struct{}) (uint64, error) {
+ requestIDs := map[uint64]struct{}{}
+ requestID := r.server.NextRequestID()
+ requestIDs[requestID] = struct{}{}
+ err := r.sendReadIndex(requestID)
+ if err != nil {
+ return 0, err
+ }
+
+ lg := r.server.Logger()
+ errorTimer := time.NewTimer(r.server.RequestTimeout())
+ defer errorTimer.Stop()
+ retryTimer := time.NewTimer(readIndexRetryTime)
+ defer retryTimer.Stop()
+
+ firstCommitInTermNotifier := r.server.FirstCommitInTermNotify()
+
+ for {
+ select {
+ case rs := <-r.raft.ReadState():
+ // Check again if leader changed as when multiple channels are ready, select picks randomly.
+ select {
+ case <-leaderChangedNotifier:
+ readIndexFailed.Inc()
+ return 0, errors.ErrLeaderChanged
+ default:
+ }
+ responseID := uint64(0)
+ if len(rs.RequestCtx) == 8 {
+ responseID = binary.BigEndian.Uint64(rs.RequestCtx)
+ }
+ if _, ok := requestIDs[responseID]; !ok {
+ // a previous request might time out. now we should ignore the response of it and
+ // continue waiting for the response of the current requests.
+ lg.Warn(
+ "ignored out-of-date read index response; local node read indexes queueing up and waiting to be in sync with leader",
+ zap.Uint64("received-request-id", responseID),
+ )
+ slowReadIndex.Inc()
+ continue
+ }
+ return rs.Index, nil
+ case <-leaderChangedNotifier:
+ readIndexFailed.Inc()
+ // return a retryable error.
+ return 0, errors.ErrLeaderChanged
+ case <-firstCommitInTermNotifier:
+ firstCommitInTermNotifier = r.server.FirstCommitInTermNotify()
+ lg.Info("first commit in current term: resending ReadIndex request")
+ requestID = r.server.NextRequestID()
+ requestIDs[requestID] = struct{}{}
+ err := r.sendReadIndex(requestID)
+ if err != nil {
+ return 0, err
+ }
+ retryTimer.Reset(readIndexRetryTime)
+ continue
+ case <-retryTimer.C:
+ lg.Warn(
+ "waiting for ReadIndex response took too long, retrying",
+ zap.Uint64("sent-request-id", requestID),
+ zap.Duration("retry-timeout", readIndexRetryTime),
+ )
+ requestID = r.server.NextRequestID()
+ requestIDs[requestID] = struct{}{}
+ err := r.sendReadIndex(requestID)
+ if err != nil {
+ return 0, err
+ }
+ retryTimer.Reset(readIndexRetryTime)
+ continue
+ case <-errorTimer.C:
+ lg.Warn(
+ "timed out waiting for read index response (local node might have slow network)",
+ zap.Duration("timeout", r.server.RequestTimeout()),
+ )
+ slowReadIndex.Inc()
+ return 0, errors.ErrTimeout
+ case <-r.server.Stopping():
+ return 0, errors.ErrStopped
+ }
+ }
+}
+
+func (r *Read) sendReadIndex(requestIndex uint64) error {
+ ctxToSend := uint64ToBigEndianBytes(requestIndex)
+
+ cctx, cancel := context.WithTimeout(context.Background(), r.server.RequestTimeout())
+ err := r.raft.ReadIndex(cctx, ctxToSend)
+ cancel()
+ if errorspkg.Is(err, raft.ErrStopped) {
+ return err
+ }
+ if err != nil {
+ lg := r.server.Logger()
+ lg.Warn("failed to get read index from Raft", zap.Error(err))
+ readIndexFailed.Inc()
+ return err
+ }
+ return nil
+}
+
+func uint64ToBigEndianBytes(number uint64) []byte {
+ byteResult := make([]byte, 8)
+ binary.BigEndian.PutUint64(byteResult, number)
+ return byteResult
+}
+
+func isStopped(err error) bool {
+ return errorspkg.Is(err, raft.ErrStopped) || errorspkg.Is(err, errors.ErrStopped)
+}
diff --git a/server/etcdserver/read/read_test.go b/server/etcdserver/read/read_test.go
new file mode 100644
index 000000000000..e212598515a3
--- /dev/null
+++ b/server/etcdserver/read/read_test.go
@@ -0,0 +1,251 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 read
+
+import (
+ "context"
+ "encoding/binary"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zaptest"
+
+ "go.etcd.io/etcd/server/v3/etcdserver/errors"
+ "go.etcd.io/raft/v3"
+)
+
+func TestRequestCurrentIndex_LeaderChangedRace(t *testing.T) {
+ s, mockRaft := setupTestRequestCurrentIndex(t)
+ r := NewRead(s, mockRaft)
+
+ for i := 0; i < 100; i++ {
+ mockRaft.readStateC <- raft.ReadState{Index: 100}
+ s.leaderChanged <- struct{}{}
+
+ index, err := r.requestCurrentIndex(s.LeaderChanged())
+ require.ErrorIs(t, err, errors.ErrLeaderChanged)
+ require.Equal(t, uint64(0), index)
+
+ select {
+ case <-mockRaft.readStateC:
+ default:
+ }
+ select {
+ case <-s.leaderChanged:
+ default:
+ }
+ }
+}
+
+func TestRequestCurrentIndex_UniqueRequestID(t *testing.T) {
+ s, mockRaft := setupTestRequestCurrentIndex(t)
+ r := NewRead(s, mockRaft)
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ r.requestCurrentIndex(s.LeaderChanged())
+ }()
+
+ require.Eventually(t, func() bool {
+ return len(mockRaft.getRequests()) >= 2
+ }, time.Second, 100*time.Millisecond)
+
+ s.leaderChanged <- struct{}{}
+ wg.Wait()
+
+ seen := make(map[uint64]bool)
+ for _, id := range mockRaft.getRequests() {
+ require.Falsef(t, seen[id], "Found duplicate request ID: %d", id)
+ seen[id] = true
+ }
+}
+
+func TestRequestCurrentIndex_Success(t *testing.T) {
+ s, mockRaft := setupTestRequestCurrentIndex(t)
+ r := NewRead(s, mockRaft)
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ var index uint64
+ var err error
+ go func() {
+ defer wg.Done()
+ index, err = r.requestCurrentIndex(s.LeaderChanged())
+ }()
+
+ require.Eventually(t, func() bool {
+ return len(mockRaft.getRequests()) == 1
+ }, time.Second, 100*time.Millisecond)
+
+ reqID := mockRaft.getRequests()[0]
+ reqIDBytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(reqIDBytes, reqID)
+
+ mockRaft.readStateC <- raft.ReadState{
+ Index: 100,
+ RequestCtx: reqIDBytes,
+ }
+
+ wg.Wait()
+
+ require.NoError(t, err)
+ require.Equal(t, uint64(100), index)
+ require.Lenf(t, mockRaft.getRequests(), 1, "Expected exactly 1 ReadIndex request")
+}
+
+func TestRequestCurrentIndex_WrongRequestID(t *testing.T) {
+ s, mockRaft := setupTestRequestCurrentIndex(t)
+ r := NewRead(s, mockRaft)
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ var index uint64
+ var err error
+ go func() {
+ defer wg.Done()
+ index, err = r.requestCurrentIndex(s.LeaderChanged())
+ }()
+
+ require.Eventually(t, func() bool {
+ return len(mockRaft.getRequests()) == 1
+ }, time.Second, 10*time.Millisecond)
+
+ wrongReqIDBytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(wrongReqIDBytes, 99999)
+
+ mockRaft.readStateC <- raft.ReadState{
+ Index: 100,
+ RequestCtx: wrongReqIDBytes,
+ }
+
+ time.Sleep(100 * time.Millisecond)
+ requests := mockRaft.getRequests()
+ require.Lenf(t, requests, 1, "Expected exactly 1 ReadIndex request")
+
+ reqID := requests[0]
+ reqIDBytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(reqIDBytes, reqID)
+
+ mockRaft.readStateC <- raft.ReadState{
+ Index: 99,
+ RequestCtx: reqIDBytes,
+ }
+ wg.Wait()
+
+ require.NoError(t, err)
+ require.Equal(t, uint64(99), index)
+ require.Lenf(t, mockRaft.getRequests(), 1, "Expected exactly 1 ReadIndex request")
+}
+
+func TestRequestCurrentIndex_DelayedResponse(t *testing.T) {
+ s, mockRaft := setupTestRequestCurrentIndex(t)
+ r := NewRead(s, mockRaft)
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ var index uint64
+ var err error
+ go func() {
+ defer wg.Done()
+ index, err = r.requestCurrentIndex(s.LeaderChanged())
+ }()
+
+ require.Eventually(t, func() bool {
+ return len(mockRaft.getRequests()) >= 3
+ }, 2*time.Second, 100*time.Millisecond)
+ requests := mockRaft.getRequests()
+
+ reqID := requests[1]
+ reqIDBytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(reqIDBytes, reqID)
+
+ select {
+ case mockRaft.readStateC <- raft.ReadState{
+ Index: 100,
+ RequestCtx: reqIDBytes,
+ }:
+ case <-time.After(time.Second):
+ t.Fatal("timed out sending read state")
+ }
+ wg.Wait()
+
+ require.NoError(t, err)
+ require.Equal(t, uint64(100), index)
+}
+
+func setupTestRequestCurrentIndex(t *testing.T) (*mockServer, *testRaftNode) {
+ s := &mockServer{
+ leaderChanged: make(chan struct{}, 1),
+ stopping: make(chan struct{}, 1),
+ done: make(chan struct{}, 1),
+ logger: zaptest.NewLogger(t),
+ timeout: 5 * time.Second,
+ firstCommit: make(chan struct{}, 1),
+ }
+ mockRaft := &testRaftNode{
+ readStateC: make(chan raft.ReadState, 1),
+ }
+ return s, mockRaft
+}
+
+type testRaftNode struct {
+ readStateC chan raft.ReadState
+ mu sync.Mutex
+ readIndexRequests []uint64
+}
+
+func (m *testRaftNode) ReadState() <-chan raft.ReadState { return m.readStateC }
+func (m *testRaftNode) ReadIndex(ctx context.Context, rctx []byte) error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if len(rctx) == 8 {
+ m.readIndexRequests = append(m.readIndexRequests, binary.BigEndian.Uint64(rctx))
+ }
+ return nil
+}
+
+func (m *testRaftNode) getRequests() []uint64 {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ res := make([]uint64, len(m.readIndexRequests))
+ copy(res, m.readIndexRequests)
+ return res
+}
+
+type mockServer struct {
+ leaderChanged chan struct{}
+ stopping chan struct{}
+ done chan struct{}
+ logger *zap.Logger
+ appliedIndex uint64
+ nextRequestID uint64
+ timeout time.Duration
+ firstCommit chan struct{}
+}
+
+func (s *mockServer) LeaderChanged() <-chan struct{} { return s.leaderChanged }
+func (s *mockServer) Stopping() <-chan struct{} { return s.stopping }
+func (s *mockServer) Logger() *zap.Logger { return s.logger }
+func (s *mockServer) AppliedIndex() uint64 { return s.appliedIndex }
+func (s *mockServer) ApplyWait(deadline uint64) <-chan struct{} { return nil }
+func (s *mockServer) NextRequestID() uint64 { s.nextRequestID++; return s.nextRequestID }
+func (s *mockServer) RequestTimeout() time.Duration { return s.timeout }
+func (s *mockServer) FirstCommitInTermNotify() <-chan struct{} { return s.firstCommit }
+func (s *mockServer) Done() <-chan struct{} { return s.done }
diff --git a/server/etcdserver/read/util.go b/server/etcdserver/read/util.go
new file mode 100644
index 000000000000..cbaf6d605bb1
--- /dev/null
+++ b/server/etcdserver/read/util.go
@@ -0,0 +1,31 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 read
+
+type notifier struct {
+ c chan struct{}
+ err error
+}
+
+func newNotifier() *notifier {
+ return ¬ifier{
+ c: make(chan struct{}),
+ }
+}
+
+func (nc *notifier) notify(err error) {
+ nc.err = err
+ close(nc.c)
+}
diff --git a/server/etcdserver/server.go b/server/etcdserver/server.go
index 8f43b54f1470..0a6ec60f92d9 100644
--- a/server/etcdserver/server.go
+++ b/server/etcdserver/server.go
@@ -22,9 +22,6 @@ import (
"fmt"
"math"
"net/http"
- "path"
- "reflect"
- "regexp"
"strconv"
"sync"
"sync/atomic"
@@ -34,6 +31,7 @@ import (
humanize "github.com/dustin/go-humanize"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/membershippb"
@@ -63,7 +61,7 @@ import (
"go.etcd.io/etcd/server/v3/etcdserver/apply"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/etcdserver/errors"
- "go.etcd.io/etcd/server/v3/etcdserver/txn"
+ "go.etcd.io/etcd/server/v3/etcdserver/read"
serverversion "go.etcd.io/etcd/server/v3/etcdserver/version"
"go.etcd.io/etcd/server/v3/features"
"go.etcd.io/etcd/server/v3/lease"
@@ -121,7 +119,6 @@ var (
monitorVersionInterval = rafthttp.ConnWriteTimeout - time.Second
recommendedMaxRequestBytesString = humanize.Bytes(uint64(recommendedMaxRequestBytes))
- storeMemberAttributeRegexp = regexp.MustCompile(path.Join(membership.StoreMembersPrefix, "[[:xdigit:]]{1,16}", "attributes"))
)
func init() {
@@ -210,11 +207,11 @@ type Server interface {
// EtcdServer is the production implementation of the Server interface
type EtcdServer struct {
// inflightSnapshots holds count the number of snapshots currently inflight.
- inflightSnapshots int64 // must use atomic operations to access; keep 64-bit aligned.
- appliedIndex uint64 // must use atomic operations to access; keep 64-bit aligned.
- committedIndex uint64 // must use atomic operations to access; keep 64-bit aligned.
- term uint64 // must use atomic operations to access; keep 64-bit aligned.
- lead uint64 // must use atomic operations to access; keep 64-bit aligned.
+ inflightSnapshots atomic.Int64
+ appliedIndex atomic.Uint64
+ committedIndex atomic.Uint64
+ term atomic.Uint64
+ lead atomic.Uint64
consistIndex cindex.ConsistentIndexer // consistIndex is used to get/set/save consistentIndex
r raftNode // uses 64-bit atomics; keep 64-bit aligned.
@@ -227,13 +224,7 @@ type EtcdServer struct {
w wait.Wait
- readMu sync.RWMutex
- // read routine notifies etcd server that it waits for reading by sending an empty struct to
- // readwaitC
- readwaitc chan struct{}
- // readNotifier is used to notify the read routine that it can process the request
- // when there is no error
- readNotifier *notifier
+ read *read.Read
// stop signals the run goroutine should shutdown.
stop chan struct{}
@@ -250,7 +241,6 @@ type EtcdServer struct {
cluster *membership.RaftCluster
- v2store v2store.Store
snapshotter *snap.Snapshotter
uberApply apply.UberApplier
@@ -268,7 +258,6 @@ type EtcdServer struct {
stats *stats.ServerStats
lstats *stats.LeaderStats
- SyncTicker *time.Ticker
// compactor is used to auto-compact the KV.
compactor v3compactor.Compactor
@@ -327,7 +316,6 @@ func NewServer(cfg config.ServerConfig) (srv *EtcdServer, err error) {
lgMu: new(sync.RWMutex),
lg: cfg.Logger,
errorc: make(chan error, 1),
- v2store: b.storage.st,
snapshotter: b.ss,
r: *b.raft.newRaftNode(b.ss, b.storage.wal.w, b.cluster.cl),
memberID: b.cluster.nodeID,
@@ -335,7 +323,6 @@ func NewServer(cfg config.ServerConfig) (srv *EtcdServer, err error) {
cluster: b.cluster.cl,
stats: sstats,
lstats: lstats,
- SyncTicker: time.NewTicker(500 * time.Millisecond),
peerRt: b.prt,
reqIDGen: idutil.NewGenerator(uint16(b.cluster.nodeID), time.Now()),
AccessController: &AccessController{CORS: cfg.CORS, HostWhitelist: cfg.HostWhitelist},
@@ -411,7 +398,7 @@ func NewServer(cfg config.ServerConfig) (srv *EtcdServer, err error) {
return lease.ErrNotPrimary
}
- srv.raftRequestOnce(ctx, pb.InternalRaftRequest{LeaseCheckpoint: cp})
+ srv.raftRequest(ctx, &pb.InternalRaftRequest{LeaseCheckpoint: cp})
return nil
})
}
@@ -547,7 +534,7 @@ func (s *EtcdServer) Start() {
s.GoAttach(func() { monitorFileDescriptor(s.Logger(), s.stopping) })
s.GoAttach(s.monitorClusterVersions)
s.GoAttach(s.monitorStorageVersion)
- s.GoAttach(s.linearizableReadLoop)
+ s.GoAttach(s.read.LinearizableReadLoop)
s.GoAttach(s.monitorKVHash)
s.GoAttach(s.monitorCompactHash)
s.GoAttach(s.monitorDowngrade)
@@ -582,8 +569,7 @@ func (s *EtcdServer) start() {
s.stop = make(chan struct{})
s.stopping = make(chan struct{}, 1)
s.ctx, s.cancel = context.WithCancel(context.Background())
- s.readwaitc = make(chan struct{}, 1)
- s.readNotifier = newNotifier()
+ s.read = read.NewRead(s, &s.r)
s.leaderChanged = notify.NewNotifier()
if s.ClusterVersion() != nil {
lg.Info(
@@ -643,7 +629,9 @@ func (s *EtcdServer) purgeFile() {
func (s *EtcdServer) Cluster() api.Cluster { return s.cluster }
-func (s *EtcdServer) ApplyWait() <-chan struct{} { return s.applyWait.Wait(s.getCommittedIndex()) }
+func (s *EtcdServer) ApplyWaitCommit() <-chan struct{} {
+ return s.applyWait.Wait(s.getCommittedIndex())
+}
type ServerPeer interface {
ServerV2
@@ -655,7 +643,7 @@ func (s *EtcdServer) LeaseHandler() http.Handler {
if s.lessor == nil {
return nil
}
- return leasehttp.NewHandler(s.lessor, s.ApplyWait)
+ return leasehttp.NewHandler(s.lessor, s.ApplyWaitCommit)
}
func (s *EtcdServer) RaftHandler() http.Handler { return s.r.transport.Handler() }
@@ -700,7 +688,7 @@ func (h *downgradeEnabledHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
defer cancel()
// serve with linearized downgrade info
- if err := h.server.linearizableReadNotify(ctx); err != nil {
+ if err := h.server.read.LinearizableReadNotify(ctx); err != nil {
http.Error(w, fmt.Sprintf("failed linearized read: %v", err),
http.StatusInternalServerError)
return
@@ -712,26 +700,26 @@ func (h *downgradeEnabledHandler) ServeHTTP(w http.ResponseWriter, r *http.Reque
// Process takes a raft message and applies it to the server's raft state
// machine, respecting any timeout of the given context.
-func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error {
+func (s *EtcdServer) Process(ctx context.Context, m *raftpb.Message) error {
lg := s.Logger()
- if s.cluster.IsIDRemoved(types.ID(m.From)) {
+ if s.cluster.IsIDRemoved(types.ID(m.GetFrom())) {
lg.Warn(
"rejected Raft message from removed member",
zap.String("local-member-id", s.MemberID().String()),
- zap.String("removed-member-id", types.ID(m.From).String()),
+ zap.String("removed-member-id", types.ID(m.GetFrom()).String()),
)
return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message from removed member")
}
- if s.MemberID() != types.ID(m.To) {
+ if s.MemberID() != types.ID(m.GetTo()) {
lg.Warn(
"rejected Raft message to mismatch member",
zap.String("local-member-id", s.MemberID().String()),
- zap.String("mismatch-member-id", types.ID(m.To).String()),
+ zap.String("mismatch-member-id", types.ID(m.GetTo()).String()),
)
return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message to mismatch member")
}
- if m.Type == raftpb.MsgApp {
- s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size())
+ if m.GetType() == raftpb.MsgApp {
+ s.stats.RecvAppendReq(types.ID(m.GetFrom()).String(), proto.Size(m))
}
return s.r.Step(ctx, m)
}
@@ -747,7 +735,7 @@ func (s *EtcdServer) ReportSnapshot(id uint64, status raft.SnapshotStatus) {
}
type etcdProgress struct {
- confState raftpb.ConfState
+ confState *raftpb.ConfState
diskSnapshotIndex uint64
memorySnapshotIndex uint64
appliedt uint64
@@ -817,10 +805,13 @@ func (s *EtcdServer) run() {
ep := etcdProgress{
confState: sn.Metadata.ConfState,
- diskSnapshotIndex: sn.Metadata.Index,
- memorySnapshotIndex: sn.Metadata.Index,
- appliedt: sn.Metadata.Term,
- appliedi: sn.Metadata.Index,
+ diskSnapshotIndex: sn.Metadata.GetIndex(),
+ memorySnapshotIndex: sn.Metadata.GetIndex(),
+ appliedt: sn.Metadata.GetTerm(),
+ appliedi: sn.Metadata.GetIndex(),
+ }
+ if ep.confState == nil {
+ panic("empty confstate")
}
defer func() {
@@ -833,8 +824,6 @@ func (s *EtcdServer) run() {
// wait for goroutines before closing raft so wal stays open
s.wg.Wait()
- s.SyncTicker.Stop()
-
// must stop raft after scheduler-- etcdserver can leak rafthttp pipelines
// by adding a peer after raft stops the transport
s.r.stop()
@@ -935,7 +924,7 @@ func (s *EtcdServer) ensureLeadership() bool {
ctx, cancel := context.WithTimeout(s.ctx, s.Cfg.ReqTimeout())
defer cancel()
- if err := s.linearizableReadNotify(ctx); err != nil {
+ if err := s.read.LinearizableReadNotify(ctx); err != nil {
lg.Warn("Failed to check current member's leadership",
zap.Error(err))
return false
@@ -1014,27 +1003,27 @@ func (s *EtcdServer) applySnapshot(ep *etcdProgress, toApply *toApply) {
"applying snapshot",
zap.Uint64("current-snapshot-index", ep.diskSnapshotIndex),
zap.Uint64("current-applied-index", ep.appliedi),
- zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.Index),
- zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.Term),
+ zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.GetIndex()),
+ zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.GetTerm()),
)
defer func() {
lg.Info(
"applied snapshot",
zap.Uint64("current-snapshot-index", ep.diskSnapshotIndex),
zap.Uint64("current-applied-index", ep.appliedi),
- zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.Index),
- zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.Term),
+ zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.GetIndex()),
+ zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.GetTerm()),
)
applySnapshotInProgress.Dec()
}()
- if toApply.snapshot.Metadata.Index <= ep.appliedi {
+ if toApply.snapshot.Metadata.GetIndex() <= ep.appliedi {
lg.Panic(
"unexpected leader snapshot from outdated index",
zap.Uint64("current-snapshot-index", ep.diskSnapshotIndex),
zap.Uint64("current-applied-index", ep.appliedi),
- zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.Index),
- zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.Term),
+ zap.Uint64("incoming-leader-snapshot-index", toApply.snapshot.Metadata.GetIndex()),
+ zap.Uint64("incoming-leader-snapshot-term", toApply.snapshot.Metadata.GetTerm()),
)
}
@@ -1119,17 +1108,6 @@ func (s *EtcdServer) applySnapshot(ep *etcdProgress, toApply *toApply) {
lg.Info("restored auth store")
}
- lg.Info("restoring v2 store")
- if err := s.v2store.Recovery(toApply.snapshot.Data); err != nil {
- lg.Panic("failed to restore v2 store", zap.Error(err))
- }
-
- if err := serverstorage.AssertNoV2StoreContent(lg, s.v2store, s.Cfg.V2Deprecation); err != nil {
- lg.Panic("illegal v2store content", zap.Error(err))
- }
-
- lg.Info("restored v2 store")
-
s.cluster.SetBackend(schema.NewMembershipBackend(lg, newbe))
lg.Info("restoring cluster configuration")
@@ -1154,8 +1132,8 @@ func (s *EtcdServer) applySnapshot(ep *etcdProgress, toApply *toApply) {
lg.Info("added peers from new cluster configuration")
- ep.appliedt = toApply.snapshot.Metadata.Term
- ep.appliedi = toApply.snapshot.Metadata.Index
+ ep.appliedt = toApply.snapshot.Metadata.GetTerm()
+ ep.appliedi = toApply.snapshot.Metadata.GetIndex()
ep.diskSnapshotIndex = ep.appliedi
ep.memorySnapshotIndex = ep.appliedi
ep.confState = toApply.snapshot.Metadata.ConfState
@@ -1166,23 +1144,41 @@ func (s *EtcdServer) applySnapshot(ep *etcdProgress, toApply *toApply) {
}
func (s *EtcdServer) NewUberApplier() apply.UberApplier {
- return apply.NewUberApplier(s.lg, s.be, s.KV(), s.alarmStore, s.authStore, s.lessor, s.cluster, s, s, s.consistIndex,
- s.Cfg.WarningApplyDuration, s.Cfg.ServerFeatureGate.Enabled(features.TxnModeWriteWithSharedBuffer), s.Cfg.QuotaBackendBytes)
-}
-
-func verifySnapshotIndex(snapshot raftpb.Snapshot, cindex uint64) {
- verify.Verify(func() {
- if cindex != snapshot.Metadata.Index {
- panic(fmt.Sprintf("consistent_index(%d) isn't equal to snapshot index (%d)", cindex, snapshot.Metadata.Index))
- }
+ opts := apply.ApplierOptions{
+ Logger: s.lg,
+ KV: s.KV(),
+ AlarmStore: s.alarmStore,
+ AuthStore: s.authStore,
+ Lessor: s.lessor,
+ Cluster: s.cluster,
+ RaftStatus: s,
+ SnapshotServer: s,
+ ConsistentIndex: s.consistIndex,
+ TxnModeWriteWithSharedBuffer: s.Cfg.ServerFeatureGate.Enabled(features.TxnModeWriteWithSharedBuffer),
+ Backend: s.be,
+ QuotaBackendBytesCfg: s.Cfg.QuotaBackendBytes,
+ WarningApplyDuration: s.Cfg.WarningApplyDuration,
+ }
+ return apply.NewUberApplier(opts)
+}
+
+func verifySnapshotIndex(snapshot *raftpb.Snapshot, cindex uint64) {
+ verify.Verify("consistent_index isn't equal to snapshot index", func() (bool, map[string]any) {
+ return cindex == snapshot.Metadata.GetIndex(),
+ map[string]any{
+ "consistent_index": cindex,
+ "snapshot_index": snapshot.Metadata.GetIndex(),
+ }
})
}
-func verifyConsistentIndexIsLatest(lg *zap.Logger, snapshot raftpb.Snapshot, cindex uint64) {
- verify.Verify(func() {
- if cindex < snapshot.Metadata.Index {
- lg.Panic(fmt.Sprintf("consistent_index(%d) is older than snapshot index (%d)", cindex, snapshot.Metadata.Index))
- }
+func verifyConsistentIndexIsLatest(snapshot *raftpb.Snapshot, cindex uint64) {
+ verify.Verify("consistent_index is older than snapshot_index", func() (bool, map[string]any) {
+ return cindex >= snapshot.Metadata.GetIndex(),
+ map[string]any{
+ "consistent_index": cindex,
+ "snapshot_index": snapshot.Metadata.GetIndex(),
+ }
})
}
@@ -1190,7 +1186,7 @@ func (s *EtcdServer) applyEntries(ep *etcdProgress, apply *toApply) {
if len(apply.entries) == 0 {
return
}
- firsti := apply.entries[0].Index
+ firsti := apply.entries[0].GetIndex()
if firsti > ep.appliedi+1 {
lg := s.Logger()
lg.Panic(
@@ -1199,7 +1195,7 @@ func (s *EtcdServer) applyEntries(ep *etcdProgress, apply *toApply) {
zap.Uint64("first-committed-entry-index", firsti),
)
}
- var ents []raftpb.Entry
+ var ents []*raftpb.Entry
if ep.appliedi+1-firsti < uint64(len(apply.entries)) {
ents = apply.entries[ep.appliedi+1-firsti:]
}
@@ -1207,7 +1203,7 @@ func (s *EtcdServer) applyEntries(ep *etcdProgress, apply *toApply) {
return
}
var shouldstop bool
- if ep.appliedt, ep.appliedi, shouldstop = s.apply(ents, &ep.confState, apply.raftAdvancedC); shouldstop {
+ if ep.appliedt, ep.appliedi, shouldstop = s.apply(ents, ep, apply.raftAdvancedC); shouldstop {
go s.stopWithDelay(10*100*time.Millisecond, fmt.Errorf("the member has been permanently removed from the cluster"))
}
}
@@ -1399,16 +1395,15 @@ func (s *EtcdServer) AddMember(ctx context.Context, memb membership.Member) ([]*
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: uint64(memb.ID),
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(memb.ID)),
Context: b,
}
-
if memb.IsLearner {
- cc.Type = raftpb.ConfChangeAddLearnerNode
+ cc.Type = raftpb.ConfChangeAddLearnerNode.Enum()
}
- return s.configure(ctx, cc)
+ return s.configure(ctx, &cc)
}
func (s *EtcdServer) mayAddMember(memb membership.Member) error {
@@ -1428,9 +1423,10 @@ func (s *EtcdServer) mayAddMember(memb membership.Member) error {
return errors.ErrNotEnoughStartedMembers
}
- if !isConnectedFullySince(s.r.transport, time.Now().Add(-HealthInterval), s.MemberID(), s.cluster.VotingMembers()) {
+ // Treat the new member as unavailable when checking quorum safety.
+ if !isConnectedToQuorumAfterAddingNewMemberSince(s.r.transport, time.Now().Add(-HealthInterval), s.MemberID(), s.cluster.VotingMembers()) {
lg.Warn(
- "rejecting member add request; local member has not been connected to all peers, reconfigure breaks active quorum",
+ "rejecting member add request; local member has not been connected to majority peers, reconfigure breaks active quorum",
zap.String("local-member-id", s.MemberID().String()),
zap.String("requested-member-add", fmt.Sprintf("%+v", memb)),
zap.Error(errors.ErrUnhealthy),
@@ -1452,10 +1448,10 @@ func (s *EtcdServer) RemoveMember(ctx context.Context, id uint64) ([]*membership
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: id,
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(id),
}
- return s.configure(ctx, cc)
+ return s.configure(ctx, &cc)
}
// PromoteMember promotes a learner node to a voting node.
@@ -1529,12 +1525,11 @@ func (s *EtcdServer) promoteMember(ctx context.Context, id uint64) ([]*membershi
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: id,
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(id),
Context: b,
}
-
- return s.configure(ctx, cc)
+ return s.configure(ctx, &cc)
}
func (s *EtcdServer) mayPromoteMember(id types.ID) error {
@@ -1663,16 +1658,16 @@ func (s *EtcdServer) UpdateMember(ctx context.Context, memb membership.Member) (
return nil, err
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: uint64(memb.ID),
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(memb.ID)),
Context: b,
}
- return s.configure(ctx, cc)
+ return s.configure(ctx, &cc)
}
func (s *EtcdServer) MemberList(ctx context.Context, r *pb.MemberListRequest) ([]*membership.Member, error) {
if r.Linearizable {
- if err := s.LinearizableReadNotify(ctx); err != nil {
+ if err := s.read.LinearizableReadNotify(ctx); err != nil {
return nil, err
}
}
@@ -1684,35 +1679,35 @@ func (s *EtcdServer) MemberList(ctx context.Context, r *pb.MemberListRequest) ([
}
func (s *EtcdServer) setCommittedIndex(v uint64) {
- atomic.StoreUint64(&s.committedIndex, v)
+ s.committedIndex.Store(v)
}
func (s *EtcdServer) getCommittedIndex() uint64 {
- return atomic.LoadUint64(&s.committedIndex)
+ return s.committedIndex.Load()
}
func (s *EtcdServer) setAppliedIndex(v uint64) {
- atomic.StoreUint64(&s.appliedIndex, v)
+ s.appliedIndex.Store(v)
}
func (s *EtcdServer) getAppliedIndex() uint64 {
- return atomic.LoadUint64(&s.appliedIndex)
+ return s.appliedIndex.Load()
}
func (s *EtcdServer) setTerm(v uint64) {
- atomic.StoreUint64(&s.term, v)
+ s.term.Store(v)
}
func (s *EtcdServer) getTerm() uint64 {
- return atomic.LoadUint64(&s.term)
+ return s.term.Load()
}
func (s *EtcdServer) setLead(v uint64) {
- atomic.StoreUint64(&s.lead, v)
+ s.lead.Store(v)
}
func (s *EtcdServer) getLead() uint64 {
- return atomic.LoadUint64(&s.lead)
+ return s.lead.Load()
}
func (s *EtcdServer) LeaderChangedNotify() <-chan struct{} {
@@ -1756,14 +1751,14 @@ type confChangeResponse struct {
// configure sends a configuration change through consensus and
// then waits for it to be applied to the server. It
// will block until the change is performed or there is an error.
-func (s *EtcdServer) configure(ctx context.Context, cc raftpb.ConfChange) ([]*membership.Member, error) {
+func (s *EtcdServer) configure(ctx context.Context, cc *raftpb.ConfChange) ([]*membership.Member, error) {
lg := s.Logger()
- cc.ID = s.reqIDGen.Next()
- ch := s.w.Register(cc.ID)
+ cc.Id = new(s.reqIDGen.Next())
+ ch := s.w.Register(cc.GetId())
start := time.Now()
if err := s.r.ProposeConfChange(ctx, cc); err != nil {
- s.w.Trigger(cc.ID, nil)
+ s.w.Trigger(cc.GetId(), nil)
return nil, err
}
@@ -1781,13 +1776,13 @@ func (s *EtcdServer) configure(ctx context.Context, cc raftpb.ConfChange) ([]*me
lg.Info(
"applied a configuration change through raft",
zap.String("local-member-id", s.MemberID().String()),
- zap.String("raft-conf-change", cc.Type.String()),
- zap.String("raft-conf-change-node-id", types.ID(cc.NodeID).String()),
+ zap.String("raft-conf-change", cc.GetType().String()),
+ zap.String("raft-conf-change-node-id", types.ID(cc.GetNodeId()).String()),
)
return resp.membs, resp.err
case <-ctx.Done():
- s.w.Trigger(cc.ID, nil) // GC wait
+ s.w.Trigger(cc.GetId(), nil) // GC wait
return nil, s.parseProposeCtxErr(ctx.Err(), start)
case <-s.stopping:
@@ -1825,7 +1820,7 @@ func (s *EtcdServer) publishV3(timeout time.Duration) {
}
ctx, cancel := context.WithTimeout(s.ctx, timeout)
- _, err := s.raftRequest(ctx, pb.InternalRaftRequest{ClusterMemberAttrSet: req})
+ _, err := s.raftRequest(ctx, &pb.InternalRaftRequest{ClusterMemberAttrSet: req})
cancel()
switch err {
case nil:
@@ -1851,13 +1846,13 @@ func (s *EtcdServer) publishV3(timeout time.Duration) {
}
}
-func (s *EtcdServer) sendMergedSnap(merged snap.Message) {
- atomic.AddInt64(&s.inflightSnapshots, 1)
+func (s *EtcdServer) sendMergedSnap(merged *snap.Message) {
+ s.inflightSnapshots.Add(1)
lg := s.Logger()
fields := []zap.Field{
zap.String("from", s.MemberID().String()),
- zap.String("to", types.ID(merged.To).String()),
+ zap.String("to", types.ID(merged.GetTo()).String()),
zap.Int64("bytes", merged.TotalSize),
zap.String("size", humanize.Bytes(uint64(merged.TotalSize))),
}
@@ -1880,7 +1875,7 @@ func (s *EtcdServer) sendMergedSnap(merged snap.Message) {
}
}
- atomic.AddInt64(&s.inflightSnapshots, -1)
+ s.inflightSnapshots.Add(-1)
lg.Info("sent merged snapshot", append(fields, zap.Duration("took", time.Since(now)))...)
@@ -1895,8 +1890,8 @@ func (s *EtcdServer) sendMergedSnap(merged snap.Message) {
// applies them to the current state of the EtcdServer.
// The given entries should not be empty.
func (s *EtcdServer) apply(
- es []raftpb.Entry,
- confState *raftpb.ConfState,
+ es []*raftpb.Entry,
+ ep *etcdProgress,
raftAdvancedC <-chan struct{},
) (appliedt uint64, appliedi uint64, shouldStop bool) {
s.lg.Debug("Applying entries", zap.Int("num-entries", len(es)))
@@ -1905,57 +1900,56 @@ func (s *EtcdServer) apply(
index := s.consistIndex.ConsistentIndex()
s.lg.Debug("Applying entry",
zap.Uint64("consistent-index", index),
- zap.Uint64("entry-index", e.Index),
- zap.Uint64("entry-term", e.Term),
- zap.Stringer("entry-type", e.Type))
+ zap.Uint64("entry-index", e.GetIndex()),
+ zap.Uint64("entry-term", e.GetTerm()),
+ zap.Stringer("entry-type", e.GetType()))
// We need to toApply all WAL entries on top of v2store
- // and only 'unapplied' (e.Index>backend.ConsistentIndex) on the backend.
+ // and only 'unapplied' (e.GetIndex()>backend.ConsistentIndex) on the backend.
shouldApplyV3 := membership.ApplyV2storeOnly
- if e.Index > index {
+ if e.GetIndex() > index {
shouldApplyV3 = membership.ApplyBoth
// set the consistent index of current executing entry
- s.consistIndex.SetConsistentApplyingIndex(e.Index, e.Term)
+ s.consistIndex.SetConsistentApplyingIndex(e.GetIndex(), e.GetTerm())
}
- switch e.Type {
+ switch e.GetType() {
case raftpb.EntryNormal:
// gofail: var beforeApplyOneEntryNormal struct{}
- s.applyEntryNormal(&e, shouldApplyV3)
- s.setAppliedIndex(e.Index)
- s.setTerm(e.Term)
+ s.applyEntryNormal(e, shouldApplyV3)
+ s.setAppliedIndex(e.GetIndex())
+ s.setTerm(e.GetTerm())
case raftpb.EntryConfChange:
// gofail: var beforeApplyOneConfChange struct{}
var cc raftpb.ConfChange
- pbutil.MustUnmarshal(&cc, e.Data)
- removedSelf, err := s.applyConfChange(cc, confState, shouldApplyV3)
- s.setAppliedIndex(e.Index)
- s.setTerm(e.Term)
+ pbutil.MustUnmarshalMessage(&cc, e.Data)
+ removedSelf, err := s.applyConfChange(&cc, ep, shouldApplyV3)
+ s.setAppliedIndex(e.GetIndex())
+ s.setTerm(e.GetTerm())
shouldStop = shouldStop || removedSelf
- s.w.Trigger(cc.ID, &confChangeResponse{s.cluster.Members(), raftAdvancedC, err})
+ s.w.Trigger(cc.GetId(), &confChangeResponse{s.cluster.Members(), raftAdvancedC, err})
default:
lg := s.Logger()
lg.Panic(
"unknown entry type; must be either EntryNormal or EntryConfChange",
- zap.String("type", e.Type.String()),
+ zap.String("type", e.GetType().String()),
)
}
- appliedi, appliedt = e.Index, e.Term
+ appliedi, appliedt = e.GetIndex(), e.GetTerm()
}
return appliedt, appliedi, shouldStop
}
// applyEntryNormal applies an EntryNormal type raftpb request to the EtcdServer
func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry, shouldApplyV3 membership.ShouldApplyV3) {
- var ar *apply.Result
if shouldApplyV3 {
defer func() {
// The txPostLockInsideApplyHook will not get called in some cases,
// in which we should move the consistent index forward directly.
newIndex := s.consistIndex.ConsistentIndex()
- if newIndex < e.Index {
- s.consistIndex.SetConsistentIndex(e.Index, e.Term)
+ if newIndex < e.GetIndex() {
+ s.consistIndex.SetConsistentIndex(e.GetIndex(), e.GetTerm())
}
}()
}
@@ -1973,36 +1967,7 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry, shouldApplyV3 membership.
return
}
- var raftReq pb.InternalRaftRequest
- if !pbutil.MaybeUnmarshal(&raftReq, e.Data) { // backward compatible
- var r pb.Request
- rp := &r
- pbutil.MustUnmarshal(rp, e.Data)
- s.lg.Debug("applyEntryNormal", zap.Stringer("V2request", rp))
- raftReq = v2ToV3Request(s.lg, (*RequestV2)(rp))
- }
- s.lg.Debug("applyEntryNormal", zap.Stringer("raftReq", &raftReq))
-
- if raftReq.V2 != nil {
- req := (*RequestV2)(raftReq.V2)
- raftReq = v2ToV3Request(s.lg, req)
- }
-
- id := raftReq.ID
- if id == 0 {
- if raftReq.Header == nil {
- s.lg.Panic("applyEntryNormal, could not find a header")
- }
- id = raftReq.Header.ID
- }
-
- needResult := s.w.IsRegistered(id)
- if needResult || !noSideEffect(&raftReq) {
- if !needResult && raftReq.Txn != nil {
- removeNeedlessRangeReqs(raftReq.Txn)
- }
- ar = s.applyInternalRaftRequest(&raftReq, shouldApplyV3)
- }
+ ar, id := apply.Apply(s.lg, e, s.uberApply, s.w, shouldApplyV3)
// do not re-toApply applied entries.
if !shouldApplyV3 {
@@ -2032,76 +1997,18 @@ func (s *EtcdServer) applyEntryNormal(e *raftpb.Entry, shouldApplyV3 membership.
Action: pb.AlarmRequest_ACTIVATE,
Alarm: pb.AlarmType_NOSPACE,
}
- s.raftRequest(s.ctx, pb.InternalRaftRequest{Alarm: a})
+ s.raftRequest(s.ctx, &pb.InternalRaftRequest{Alarm: a})
s.w.Trigger(id, ar)
})
}
-func (s *EtcdServer) applyInternalRaftRequest(r *pb.InternalRaftRequest, shouldApplyV3 membership.ShouldApplyV3) *apply.Result {
- if r.ClusterVersionSet == nil && r.ClusterMemberAttrSet == nil && r.DowngradeInfoSet == nil && r.DowngradeVersionTest == nil {
- if !shouldApplyV3 {
- return nil
- }
- return s.uberApply.Apply(r)
- }
- membershipApplier := apply.NewApplierMembership(s.lg, s.cluster, s)
- op := "unknown"
- defer func(start time.Time) {
- txn.ApplySecObserve("v3", op, true, time.Since(start))
- txn.WarnOfExpensiveRequest(s.lg, s.Cfg.WarningApplyDuration, start, &pb.InternalRaftStringer{Request: r}, nil, nil)
- }(time.Now())
- switch {
- case r.ClusterVersionSet != nil:
- op = "ClusterVersionSet" // Implemented in 3.5.x
- membershipApplier.ClusterVersionSet(r.ClusterVersionSet, shouldApplyV3)
- return &apply.Result{}
- case r.ClusterMemberAttrSet != nil:
- op = "ClusterMemberAttrSet" // Implemented in 3.5.x
- membershipApplier.ClusterMemberAttrSet(r.ClusterMemberAttrSet, shouldApplyV3)
- case r.DowngradeInfoSet != nil:
- op = "DowngradeInfoSet" // Implemented in 3.5.x
- membershipApplier.DowngradeInfoSet(r.DowngradeInfoSet, shouldApplyV3)
- case r.DowngradeVersionTest != nil:
- op = "DowngradeVersionTest" // Implemented in 3.6 for test only
- // do nothing, we are just to ensure etcdserver don't panic in case
- // users(test cases) intentionally inject DowngradeVersionTestRequest
- // into the WAL files.
- default:
- s.lg.Panic("not implemented apply", zap.Stringer("raft-request", r))
- return nil
- }
- return &apply.Result{}
-}
-
-func noSideEffect(r *pb.InternalRaftRequest) bool {
- return r.Range != nil || r.AuthUserGet != nil || r.AuthRoleGet != nil || r.AuthStatus != nil
-}
-
-func removeNeedlessRangeReqs(txn *pb.TxnRequest) {
- f := func(ops []*pb.RequestOp) []*pb.RequestOp {
- j := 0
- for i := 0; i < len(ops); i++ {
- if _, ok := ops[i].Request.(*pb.RequestOp_RequestRange); ok {
- continue
- }
- ops[j] = ops[i]
- j++
- }
-
- return ops[:j]
- }
-
- txn.Success = f(txn.Success)
- txn.Failure = f(txn.Failure)
-}
-
// applyConfChange applies a ConfChange to the server. It is only
// invoked with a ConfChange that has already passed through Raft
-func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.ConfState, shouldApplyV3 membership.ShouldApplyV3) (bool, error) {
+func (s *EtcdServer) applyConfChange(cc *raftpb.ConfChange, ep *etcdProgress, shouldApplyV3 membership.ShouldApplyV3) (bool, error) {
lg := s.Logger()
if err := s.cluster.ValidateConfigurationChange(cc, shouldApplyV3); err != nil {
lg.Error("Validation on configuration change failed", zap.Bool("shouldApplyV3", bool(shouldApplyV3)), zap.Error(err))
- cc.NodeID = raft.None
+ cc.NodeId = new(raft.None)
s.r.ApplyConfChange(cc)
// The txPostLock callback will not get called in this case,
@@ -2113,18 +2020,24 @@ func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.Con
return false, err
}
- *confState = *s.r.ApplyConfChange(cc)
- s.beHooks.SetConfState(confState)
- switch cc.Type {
+ // We don't validate the configuration change when `shouldApplyV3`
+ // is false, so we shouldn't apply it to raft either in this case.
+ // Otherwise, we might apply an invalid confChange (which failed
+ // the validation previously) to raft on bootstrap.
+ if shouldApplyV3 {
+ ep.confState = s.r.ApplyConfChange(cc)
+ }
+ s.beHooks.SetConfState(ep.confState)
+ switch cc.GetType() {
case raftpb.ConfChangeAddNode, raftpb.ConfChangeAddLearnerNode:
confChangeContext := new(membership.ConfigChangeContext)
if err := json.Unmarshal(cc.Context, confChangeContext); err != nil {
lg.Panic("failed to unmarshal member", zap.Error(err))
}
- if cc.NodeID != uint64(confChangeContext.Member.ID) {
+ if cc.GetNodeId() != uint64(confChangeContext.Member.ID) {
lg.Panic(
"got different member ID",
- zap.String("member-id-from-config-change-entry", types.ID(cc.NodeID).String()),
+ zap.String("member-id-from-config-change-entry", types.ID(cc.GetNodeId()).String()),
zap.String("member-id-from-message", confChangeContext.Member.ID.String()),
)
}
@@ -2139,7 +2052,7 @@ func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.Con
}
case raftpb.ConfChangeRemoveNode:
- id := types.ID(cc.NodeID)
+ id := types.ID(cc.GetNodeId())
s.cluster.RemoveMember(id, shouldApplyV3)
if id == s.MemberID() {
return true, nil
@@ -2151,10 +2064,10 @@ func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.Con
if err := json.Unmarshal(cc.Context, m); err != nil {
lg.Panic("failed to unmarshal member", zap.Error(err))
}
- if cc.NodeID != uint64(m.ID) {
+ if cc.GetNodeId() != uint64(m.ID) {
lg.Panic(
"got different member ID",
- zap.String("member-id-from-config-change-entry", types.ID(cc.NodeID).String()),
+ zap.String("member-id-from-config-change-entry", types.ID(cc.GetNodeId()).String()),
zap.String("member-id-from-message", m.ID.String()),
)
}
@@ -2163,49 +2076,9 @@ func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.Con
s.r.transport.UpdatePeer(m.ID, m.PeerURLs)
}
}
-
- verify.Verify(func() {
- s.verifyV3StoreInSyncWithV2Store(shouldApplyV3)
- })
-
return false, nil
}
-func (s *EtcdServer) verifyV3StoreInSyncWithV2Store(shouldApplyV3 membership.ShouldApplyV3) {
- // If shouldApplyV3 == false, then it means v2store hasn't caught up with v3store.
- if !shouldApplyV3 {
- return
- }
-
- // clean up the Attributes, and we only care about the RaftAttributes
- cleanAttributesFunc := func(members map[types.ID]*membership.Member) map[types.ID]*membership.Member {
- processedMembers := make(map[types.ID]*membership.Member)
- for id, m := range members {
- clonedMember := m.Clone()
- clonedMember.Attributes = membership.Attributes{}
- processedMembers[id] = clonedMember
- }
-
- return processedMembers
- }
-
- v2Members, _ := s.cluster.MembersFromStore()
- v3Members, _ := s.cluster.MembersFromBackend()
-
- processedV2Members := cleanAttributesFunc(v2Members)
- processedV3Members := cleanAttributesFunc(v3Members)
-
- if match := reflect.DeepEqual(processedV2Members, processedV3Members); !match {
- v2Data, v2Err := json.Marshal(processedV2Members)
- v3Data, v3Err := json.Marshal(processedV3Members)
-
- if v2Err != nil || v3Err != nil {
- panic("members in v2store doesn't match v3store")
- }
- panic(fmt.Sprintf("members in v2store doesn't match v3store, v2store: %s, v3store: %s", string(v2Data), string(v3Data)))
- }
-}
-
// TODO: non-blocking snapshot
func (s *EtcdServer) snapshot(ep *etcdProgress, toDisk bool) {
lg := s.Logger()
@@ -2233,7 +2106,7 @@ func (s *EtcdServer) snapshot(ep *etcdProgress, toDisk bool) {
}
// For backward compatibility, generate v2 snapshot from v3 state.
- snap, err := s.r.raftStorage.CreateSnapshot(ep.appliedi, &ep.confState, d)
+ snap, err := s.r.raftStorage.CreateSnapshot(ep.appliedi, ep.confState, d)
if err != nil {
// the snapshot was done asynchronously with the progress of raft.
// raft might have already got a newer snapshot.
@@ -2244,7 +2117,7 @@ func (s *EtcdServer) snapshot(ep *etcdProgress, toDisk bool) {
}
ep.memorySnapshotIndex = ep.appliedi
- verifyConsistentIndexIsLatest(lg, snap, s.consistIndex.ConsistentIndex())
+ verifyConsistentIndexIsLatest(snap, s.consistIndex.ConsistentIndex())
if toDisk {
// SaveSnap saves the snapshot to file and appends the corresponding WAL entry.
@@ -2258,7 +2131,7 @@ func (s *EtcdServer) snapshot(ep *etcdProgress, toDisk bool) {
lg.Info(
"saved snapshot to disk",
- zap.Uint64("snapshot-index", snap.Metadata.Index),
+ zap.Uint64("snapshot-index", snap.Metadata.GetIndex()),
)
}
}
@@ -2271,7 +2144,7 @@ func (s *EtcdServer) compactRaftLog(snapi uint64) {
// the snapshot sent to catch up. If we do not pause compaction, the log entries right after
// the snapshot sent might already be compacted. It happens when the snapshot takes long time
// to send and save. Pausing compaction avoids triggering a snapshot sending cycle.
- if atomic.LoadInt64(&s.inflightSnapshots) != 0 {
+ if s.inflightSnapshots.Load() != 0 {
lg.Info("skip compaction since there is an inflight snapshot")
return
}
@@ -2445,7 +2318,7 @@ func (s *EtcdServer) updateClusterVersionV3(ver string) {
req := membershippb.ClusterVersionSetRequest{Ver: ver}
ctx, cancel := context.WithTimeout(s.ctx, s.Cfg.ReqTimeout())
- _, err := s.raftRequest(ctx, pb.InternalRaftRequest{ClusterVersionSet: &req})
+ _, err := s.raftRequest(ctx, &pb.InternalRaftRequest{ClusterVersionSet: &req})
cancel()
switch {
diff --git a/server/etcdserver/server_test.go b/server/etcdserver/server_test.go
index 2d7a647ac6f4..97e2068c7df8 100644
--- a/server/etcdserver/server_test.go
+++ b/server/etcdserver/server_test.go
@@ -16,7 +16,6 @@ package etcdserver
import (
"context"
- "encoding/binary"
"encoding/json"
errorspkg "errors"
"fmt"
@@ -30,18 +29,19 @@ import (
"testing"
"time"
- "github.com/coreos/go-semver/semver"
- "github.com/golang/protobuf/proto"
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"github.com/prometheus/client_golang/prometheus"
ptestutil "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/testing/protocmp"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/membershippb"
- "go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/client/pkg/v3/testutil"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -53,12 +53,9 @@ import (
"go.etcd.io/etcd/pkg/v3/wait"
"go.etcd.io/etcd/server/v3/auth"
"go.etcd.io/etcd/server/v3/config"
- "go.etcd.io/etcd/server/v3/etcdserver/api"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
apply2 "go.etcd.io/etcd/server/v3/etcdserver/apply"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/etcdserver/errors"
@@ -68,7 +65,6 @@ import (
"go.etcd.io/etcd/server/v3/mock/mockstore"
"go.etcd.io/etcd/server/v3/mock/mockwait"
serverstorage "go.etcd.io/etcd/server/v3/storage"
- "go.etcd.io/etcd/server/v3/storage/backend"
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
"go.etcd.io/etcd/server/v3/storage/mvcc"
"go.etcd.io/etcd/server/v3/storage/schema"
@@ -84,8 +80,6 @@ func TestApplyRepeat(t *testing.T) {
SoftState: &raft.SoftState{RaftState: raft.StateLeader},
}
cl := newTestCluster(t)
- st := v2store.New()
- cl.SetStore(v2store.New())
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -102,10 +96,8 @@ func TestApplyRepeat(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
r: *r,
- v2store: st,
cluster: cl,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
consistIndex: cindex.NewFakeConsistentIndex(0),
uberApply: uberApplierMock{},
}
@@ -114,17 +106,17 @@ func TestApplyRepeat(t *testing.T) {
Header: &pb.RequestHeader{ID: 1},
Put: &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")},
}
- ents := []raftpb.Entry{{Index: 1, Data: pbutil.MustMarshal(req)}}
+ ents := []*raftpb.Entry{{Index: new(uint64(1)), Data: pbutil.MustMarshalMessage(req)}}
n.readyc <- raft.Ready{CommittedEntries: ents}
// dup msg
n.readyc <- raft.Ready{CommittedEntries: ents}
// use a conf change to block until dup msgs are all processed
- cc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
- ents = []raftpb.Entry{{
- Index: 2,
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(cc),
+ cc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode.Enum(), NodeId: new(uint64(2))}
+ ents = []*raftpb.Entry{{
+ Index: new(uint64(2)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(cc),
}}
n.readyc <- raft.Ready{CommittedEntries: ents}
// wait for conf change message
@@ -150,101 +142,10 @@ func TestApplyRepeat(t *testing.T) {
type uberApplierMock struct{}
-func (uberApplierMock) Apply(r *pb.InternalRaftRequest) *apply2.Result {
+func (uberApplierMock) Apply(r *apply2.InternalRaftRequestWrapper, shouldApplyV3 membership.ShouldApplyV3) *apply2.Result {
return &apply2.Result{}
}
-// TestV2SetMemberAttributes validates support of hybrid v3.5 cluster which still uses v2 request.
-// TODO: Remove in v3.7
-func TestV2SetMemberAttributes(t *testing.T) {
- be, _ := betesting.NewDefaultTmpBackend(t)
- defer betesting.Close(t, be)
- cl := newTestClusterWithBackend(t, []*membership.Member{{ID: 1}}, be)
-
- cfg := config.ServerConfig{
- ServerFeatureGate: features.NewDefaultServerFeatureGate("test", nil),
- }
-
- srv := &EtcdServer{
- lgMu: new(sync.RWMutex),
- lg: zaptest.NewLogger(t),
- v2store: mockstore.NewRecorder(),
- cluster: cl,
- consistIndex: cindex.NewConsistentIndex(be),
- w: wait.New(),
- Cfg: cfg,
- }
- as, err := v3alarm.NewAlarmStore(srv.lg, schema.NewAlarmBackend(srv.lg, be))
- if err != nil {
- t.Fatal(err)
- }
- srv.alarmStore = as
- srv.uberApply = srv.NewUberApplier()
-
- req := pb.Request{
- Method: "PUT",
- ID: 1,
- Path: membership.MemberAttributesStorePath(1),
- Val: `{"Name":"abc","ClientURLs":["http://127.0.0.1:2379"]}`,
- }
- data, err := proto.Marshal(&req)
- if err != nil {
- t.Fatal(err)
- }
- srv.applyEntryNormal(&raftpb.Entry{
- Data: data,
- }, membership.ApplyV2storeOnly)
- w := membership.Attributes{Name: "abc", ClientURLs: []string{"http://127.0.0.1:2379"}}
- if g := cl.Member(1).Attributes; !reflect.DeepEqual(g, w) {
- t.Errorf("attributes = %v, want %v", g, w)
- }
-}
-
-// TestV2SetClusterVersion validates support of hybrid v3.5 cluster which still uses v2 request.
-// TODO: Remove in v3.7
-func TestV2SetClusterVersion(t *testing.T) {
- be, _ := betesting.NewDefaultTmpBackend(t)
- defer betesting.Close(t, be)
- cl := newTestClusterWithBackend(t, []*membership.Member{}, be)
- cl.SetVersion(semver.New("3.4.0"), api.UpdateCapability, membership.ApplyBoth)
- cfg := config.ServerConfig{
- ServerFeatureGate: features.NewDefaultServerFeatureGate("test", nil),
- }
-
- srv := &EtcdServer{
- lgMu: new(sync.RWMutex),
- lg: zaptest.NewLogger(t),
- v2store: mockstore.NewRecorder(),
- cluster: cl,
- consistIndex: cindex.NewConsistentIndex(be),
- w: wait.New(),
- Cfg: cfg,
- }
- as, err := v3alarm.NewAlarmStore(srv.lg, schema.NewAlarmBackend(srv.lg, be))
- if err != nil {
- t.Fatal(err)
- }
- srv.alarmStore = as
- srv.uberApply = srv.NewUberApplier()
-
- req := pb.Request{
- Method: "PUT",
- ID: 1,
- Path: membership.StoreClusterVersionKey(),
- Val: "3.5.0",
- }
- data, err := proto.Marshal(&req)
- if err != nil {
- t.Fatal(err)
- }
- srv.applyEntryNormal(&raftpb.Entry{
- Data: data,
- }, membership.ApplyV2storeOnly)
- if g := cl.Version(); !reflect.DeepEqual(*g, version.V3_5) {
- t.Errorf("attributes = %v, want %v", *g, version.V3_5)
- }
-}
-
func TestApplyConfStateWithRestart(t *testing.T) {
n := newNodeRecorder()
srv := newServer(t, n)
@@ -258,79 +159,91 @@ func TestApplyConfStateWithRestart(t *testing.T) {
t.Fatal(err)
}
- entries := []raftpb.Entry{
+ entries := []*raftpb.Entry{
{
- Term: 1,
- Index: 1,
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(&raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: nodeID,
+ Term: new(uint64(1)),
+ Index: new(uint64(1)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(&raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(1)),
Context: memberData,
}),
},
{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(&raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: nodeID,
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(&raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(1)),
}),
},
{
- Term: 1,
- Index: 3,
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(&raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: nodeID,
+ Term: new(uint64(1)),
+ Index: new(uint64(3)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(&raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(1)),
Context: memberData,
}),
},
}
+
want := []testutil.Action{
{
Name: "ApplyConfChange",
- Params: []any{raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: nodeID,
+ Params: []any{&raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(1)),
Context: memberData,
}},
},
{
Name: "ApplyConfChange",
- Params: []any{raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: nodeID,
+ Params: []any{&raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(1)),
}},
},
// This action is expected to fail validation, thus NodeID is set to 0
{
Name: "ApplyConfChange",
- Params: []any{raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
+ Params: []any{&raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
Context: memberData,
- NodeID: 0,
+ NodeId: new(uint64(0)),
}},
},
}
- confState := raftpb.ConfState{}
+ ep := &etcdProgress{
+ confState: &raftpb.ConfState{},
+ }
t.Log("Applying entries for the first time")
- srv.apply(entries, &confState, nil)
- if got, _ := n.Wait(len(want)); !reflect.DeepEqual(got, want) {
- t.Errorf("actions don't match\n got %+v\n want %+v", got, want)
+ srv.apply(entries, ep, nil)
+ got, _ := n.Wait(len(want))
+ if diff := cmp.Diff(want, got, protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" {
+ t.Errorf("actions don't match (-want +got):\n%s", diff)
}
- t.Log("Simulating etcd restart by clearing v2 store")
- srv.cluster.SetStore(v2store.New())
+ t.Log("Simulating etcd restart by clearing v3 store")
+ be, _ := betesting.NewDefaultTmpBackend(t)
+ t.Cleanup(func() {
+ betesting.Close(t, be)
+ })
+ lg := zaptest.NewLogger(t)
+ srv.cluster.SetBackend(schema.NewMembershipBackend(lg, be))
+ srv.beHooks = serverstorage.NewBackendHooks(lg, srv.consistIndex)
+ srv.consistIndex.SetBackend(be)
t.Log("Reapplying same entries after restart")
- srv.apply(entries, &confState, nil)
- if got, _ := n.Wait(2 * len(want)); !reflect.DeepEqual(got[len(want):], want) {
- t.Errorf("actions don't match\n got %+v\n want %+v", got, want)
+ srv.apply(entries, ep, nil)
+ got, _ = n.Wait(2 * len(want))
+ if diff := cmp.Diff(want, got[len(want):], protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" {
+ t.Errorf("actions don't match (-want +got):\n%s", diff)
}
}
@@ -343,12 +256,11 @@ func newServer(t *testing.T, recorder *nodeRecorder) *EtcdServer {
srv := &EtcdServer{
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
- r: *newRaftNode(raftNodeConfig{lg: lg, Node: recorder}),
+ r: *newRaftNode(raftNodeConfig{lg: lg, Node: recorder, storage: mockstorage.NewStorageRecorder("")}),
cluster: membership.NewCluster(lg),
consistIndex: cindex.NewConsistentIndex(be),
}
srv.cluster.SetBackend(schema.NewMembershipBackend(lg, be))
- srv.cluster.SetStore(v2store.New())
srv.beHooks = serverstorage.NewBackendHooks(lg, srv.consistIndex)
srv.r.transport = newNopTransporter()
srv.w = mockwait.NewNop()
@@ -362,7 +274,6 @@ func TestApplyConfChangeError(t *testing.T) {
cl := membership.NewCluster(lg)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
- cl.SetStore(v2store.New())
for i := 1; i <= 4; i++ {
cl.AddMember(&membership.Member{ID: types.ID(i)}, true)
@@ -388,37 +299,37 @@ func TestApplyConfChangeError(t *testing.T) {
}
tests := []struct {
- cc raftpb.ConfChange
+ cc *raftpb.ConfChange
werr error
}{
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 4,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(4)),
Context: ctx4,
},
membership.ErrIDRemoved,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeUpdateNode,
- NodeID: 4,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode.Enum(),
+ NodeId: new(uint64(4)),
Context: ctx4,
},
membership.ErrIDRemoved,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: 1,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: new(uint64(1)),
Context: ctx,
},
membership.ErrIDExists,
},
{
- raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: 5,
+ &raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(5)),
Context: ctx5,
},
membership.ErrIDNotFound,
@@ -429,22 +340,23 @@ func TestApplyConfChangeError(t *testing.T) {
srv := &EtcdServer{
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
- r: *newRaftNode(raftNodeConfig{lg: zaptest.NewLogger(t), Node: n}),
+ r: *newRaftNode(raftNodeConfig{lg: zaptest.NewLogger(t), Node: n, storage: mockstorage.NewStorageRecorder("")}),
cluster: cl,
}
_, err := srv.applyConfChange(tt.cc, nil, true)
if !errorspkg.Is(err, tt.werr) {
t.Errorf("#%d: applyConfChange error = %v, want %v", i, err, tt.werr)
}
- cc := raftpb.ConfChange{Type: tt.cc.Type, NodeID: raft.None, Context: tt.cc.Context}
+ cc := raftpb.ConfChange{Type: tt.cc.Type, NodeId: new(raft.None), Context: tt.cc.Context}
w := []testutil.Action{
{
Name: "ApplyConfChange",
- Params: []any{cc},
+ Params: []any{&cc},
},
}
- if g, _ := n.Wait(1); !reflect.DeepEqual(g, w) {
- t.Errorf("#%d: action = %+v, want %+v", i, g, w)
+ g, _ := n.Wait(1)
+ if diff := cmp.Diff(w, g, protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" {
+ t.Errorf("#%d: action mismatch (-want +got):\n%s", i, diff)
}
}
}
@@ -456,7 +368,6 @@ func TestApplyConfChangeShouldStop(t *testing.T) {
cl := membership.NewCluster(lg)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
- cl.SetStore(v2store.New())
for i := 1; i <= 3; i++ {
cl.AddMember(&membership.Member{ID: types.ID(i)}, true)
@@ -464,6 +375,7 @@ func TestApplyConfChangeShouldStop(t *testing.T) {
r := newRaftNode(raftNodeConfig{
lg: zaptest.NewLogger(t),
Node: newNodeNop(),
+ storage: mockstorage.NewStorageRecorder(""),
transport: newNopTransporter(),
})
srv := &EtcdServer{
@@ -475,11 +387,14 @@ func TestApplyConfChangeShouldStop(t *testing.T) {
beHooks: serverstorage.NewBackendHooks(lg, nil),
}
cc := raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: 2,
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(2)),
+ }
+ ep := &etcdProgress{
+ confState: &raftpb.ConfState{},
}
// remove non-local member
- shouldStop, err := srv.applyConfChange(cc, &raftpb.ConfState{}, true)
+ shouldStop, err := srv.applyConfChange(&cc, ep, true)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
@@ -488,8 +403,8 @@ func TestApplyConfChangeShouldStop(t *testing.T) {
}
// remove local member
- cc.NodeID = 1
- shouldStop, err = srv.applyConfChange(cc, &raftpb.ConfState{}, true)
+ cc.NodeId = new(uint64(1))
+ shouldStop, err = srv.applyConfChange(&cc, ep, true)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
@@ -506,7 +421,6 @@ func TestApplyConfigChangeUpdatesConsistIndex(t *testing.T) {
defer betesting.Close(t, be)
cl := membership.NewCluster(zaptest.NewLogger(t))
- cl.SetStore(v2store.New())
cl.SetBackend(schema.NewMembershipBackend(lg, be))
cl.AddMember(&membership.Member{ID: types.ID(1)}, true)
@@ -538,17 +452,20 @@ func TestApplyConfigChangeUpdatesConsistIndex(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- cc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 2, Context: b}
- ents := []raftpb.Entry{{
- Index: 2,
- Term: 4,
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(cc),
+ cc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode.Enum(), NodeId: new(uint64(2)), Context: b}
+ ents := []*raftpb.Entry{{
+ Index: new(uint64(2)),
+ Term: new(uint64(4)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(cc),
}}
+ ep := &etcdProgress{
+ confState: &raftpb.ConfState{},
+ }
raftAdvancedC := make(chan struct{}, 1)
raftAdvancedC <- struct{}{}
- _, appliedi, _ := srv.apply(ents, &raftpb.ConfState{}, raftAdvancedC)
+ _, appliedi, _ := srv.apply(ents, ep, raftAdvancedC)
consistIndex := srv.consistIndex.ConsistentIndex()
assert.Equal(t, uint64(2), appliedi)
@@ -557,7 +474,7 @@ func TestApplyConfigChangeUpdatesConsistIndex(t *testing.T) {
tx.Lock()
defer tx.Unlock()
srv.beHooks.OnPreCommitUnsafe(tx)
- assert.Equal(t, raftpb.ConfState{Voters: []uint64{2}}, *schema.UnsafeConfStateFromBackend(lg, tx))
+ assert.Equal(t, raftpb.ConfState{Voters: []uint64{2}, AutoLeave: new(false)}, *schema.UnsafeConfStateFromBackend(lg, tx))
})
rindex, _ := schema.ReadConsistentIndex(be.ReadTx())
assert.Equal(t, consistIndex, rindex)
@@ -565,9 +482,9 @@ func TestApplyConfigChangeUpdatesConsistIndex(t *testing.T) {
func realisticRaftNode(lg *zap.Logger, id uint64, snap *raftpb.Snapshot) *raftNode {
storage := raft.NewMemoryStorage()
- storage.SetHardState(raftpb.HardState{Commit: 0, Term: 0})
+ storage.SetHardState(&raftpb.HardState{Commit: new(uint64(0)), Term: new(uint64(0))})
if snap != nil {
- err := storage.ApplySnapshot(*snap)
+ err := storage.ApplySnapshot(snap)
if err != nil {
panic(err)
}
@@ -584,6 +501,7 @@ func realisticRaftNode(lg *zap.Logger, id uint64, snap *raftpb.Snapshot) *raftNo
r := newRaftNode(raftNodeConfig{
lg: lg,
Node: n,
+ storage: mockstorage.NewStorageRecorder(""),
transport: newNopTransporter(),
})
return r
@@ -594,7 +512,6 @@ func realisticRaftNode(lg *zap.Logger, id uint64, snap *raftpb.Snapshot) *raftNo
func TestApplyMultiConfChangeShouldStop(t *testing.T) {
lg := zaptest.NewLogger(t)
cl := membership.NewCluster(lg)
- cl.SetStore(v2store.New())
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -605,6 +522,7 @@ func TestApplyMultiConfChangeShouldStop(t *testing.T) {
r := newRaftNode(raftNodeConfig{
lg: lg,
Node: newNodeNop(),
+ storage: mockstorage.NewStorageRecorder(""),
transport: newNopTransporter(),
})
ci := cindex.NewFakeConsistentIndex(0)
@@ -618,24 +536,26 @@ func TestApplyMultiConfChangeShouldStop(t *testing.T) {
consistIndex: ci,
beHooks: serverstorage.NewBackendHooks(lg, ci),
}
- var ents []raftpb.Entry
+ var ents []*raftpb.Entry
for i := 1; i <= 4; i++ {
- ent := raftpb.Entry{
- Term: 1,
- Index: uint64(i),
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(
+ ents = append(ents, &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(i)),
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(
&raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: uint64(i),
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64(i)),
}),
- }
- ents = append(ents, ent)
+ })
+ }
+ ep := &etcdProgress{
+ confState: &raftpb.ConfState{},
}
raftAdvancedC := make(chan struct{}, 1)
raftAdvancedC <- struct{}{}
- _, _, shouldStop := srv.apply(ents, &raftpb.ConfState{}, raftAdvancedC)
+ _, _, shouldStop := srv.apply(ents, ep, raftAdvancedC)
if !shouldStop {
t.Errorf("shouldStop = %t, want %t", shouldStop, true)
}
@@ -650,7 +570,7 @@ func TestSnapshotDisk(t *testing.T) {
defer betesting.Close(t, be)
s := raft.NewMemoryStorage()
- s.Append([]raftpb.Entry{{Index: 1}})
+ s.Append([]*raftpb.Entry{{Index: new(uint64(1))}})
st := mockstore.NewRecorderStream()
p := mockstorage.NewStorageRecorderStream("")
r := newRaftNode(raftNodeConfig{
@@ -663,7 +583,6 @@ func TestSnapshotDisk(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
r: *r,
- v2store: st,
consistIndex: cindex.NewConsistentIndex(be),
}
srv.kv = mvcc.New(zaptest.NewLogger(t), be, &lease.FakeLessor{}, mvcc.StoreConfig{})
@@ -685,7 +604,7 @@ func TestSnapshotDisk(t *testing.T) {
assert.Equal(t, testutil.Action{Name: "SaveSnap"}, gaction[0])
assert.Equal(t, testutil.Action{Name: "Release"}, gaction[1])
}()
- ep := etcdProgress{appliedi: 1, confState: raftpb.ConfState{Voters: []uint64{1}}}
+ ep := etcdProgress{appliedi: 1, confState: &raftpb.ConfState{Voters: []uint64{1}}}
srv.snapshot(&ep, true)
<-ch
assert.Empty(t, st.Action())
@@ -701,7 +620,7 @@ func TestSnapshotMemory(t *testing.T) {
defer betesting.Close(t, be)
s := raft.NewMemoryStorage()
- s.Append([]raftpb.Entry{{Index: 1}})
+ s.Append([]*raftpb.Entry{{Index: new(uint64(1))}})
st := mockstore.NewRecorderStream()
p := mockstorage.NewStorageRecorderStream("")
r := newRaftNode(raftNodeConfig{
@@ -714,7 +633,6 @@ func TestSnapshotMemory(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
r: *r,
- v2store: st,
consistIndex: cindex.NewConsistentIndex(be),
}
srv.kv = mvcc.New(zaptest.NewLogger(t), be, &lease.FakeLessor{}, mvcc.StoreConfig{})
@@ -734,7 +652,7 @@ func TestSnapshotMemory(t *testing.T) {
assert.Empty(t, gaction)
}()
- ep := etcdProgress{appliedi: 1, confState: raftpb.ConfState{Voters: []uint64{1}}}
+ ep := etcdProgress{appliedi: 1, confState: &raftpb.ConfState{Voters: []uint64{1}}}
srv.snapshot(&ep, false)
<-ch
assert.Empty(t, st.Action())
@@ -752,7 +670,6 @@ func TestSnapshotOrdering(t *testing.T) {
lg := zaptest.NewLogger(t)
n := newNopReadyNode()
- st := v2store.New()
cl := membership.NewCluster(lg)
be, _ := betesting.NewDefaultTmpBackend(t)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -788,10 +705,8 @@ func TestSnapshotOrdering(t *testing.T) {
lg: lg,
Cfg: cfg,
r: *r,
- v2store: st,
snapshotter: snap.New(lg, snapdir),
cluster: cl,
- SyncTicker: &time.Ticker{},
consistIndex: ci,
beHooks: serverstorage.NewBackendHooks(lg, ci),
}
@@ -802,14 +717,14 @@ func TestSnapshotOrdering(t *testing.T) {
s.start()
defer s.Stop()
- n.readyc <- raft.Ready{Messages: []raftpb.Message{{Type: raftpb.MsgSnap}}}
+ n.readyc <- raft.Ready{Messages: []*raftpb.Message{{Type: raftpb.MsgSnap.Enum()}}}
go func() {
// get the snapshot sent by the transport
snapMsg := <-snapDoneC
// Snapshot first triggers raftnode to persists the snapshot onto disk
// before renaming db snapshot file to db
- snapMsg.Snapshot.Metadata.Index = 1
- n.readyc <- raft.Ready{Snapshot: *snapMsg.Snapshot}
+ snapMsg.Snapshot.Metadata.Index = new(uint64(1))
+ n.readyc <- raft.Ready{Snapshot: snapMsg.Snapshot}
}()
ac := <-p.Chan()
@@ -851,9 +766,7 @@ func TestConcurrentApplyAndSnapshotV3(t *testing.T) {
lg := zaptest.NewLogger(t)
n := newNopReadyNode()
- st := v2store.New()
cl := membership.NewCluster(lg)
- cl.SetStore(st)
be, _ := betesting.NewDefaultTmpBackend(t)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -883,10 +796,8 @@ func TestConcurrentApplyAndSnapshotV3(t *testing.T) {
ServerFeatureGate: features.NewDefaultServerFeatureGate("test", lg),
},
r: *r,
- v2store: st,
snapshotter: snap.New(lg, testdir),
cluster: cl,
- SyncTicker: &time.Ticker{},
consistIndex: ci,
beHooks: serverstorage.NewBackendHooks(lg, ci),
firstCommitInTerm: notify.NewNotifier(),
@@ -912,11 +823,11 @@ func TestConcurrentApplyAndSnapshotV3(t *testing.T) {
Header: &pb.RequestHeader{ID: idx},
Put: &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")},
}
- ent := raftpb.Entry{Index: idx, Data: pbutil.MustMarshal(req)}
- ready := raft.Ready{Entries: []raftpb.Entry{ent}}
+ ent := raftpb.Entry{Index: new(idx), Data: pbutil.MustMarshalMessage(req)}
+ ready := raft.Ready{Entries: []*raftpb.Entry{&ent}}
n.readyc <- ready
- ready = raft.Ready{CommittedEntries: []raftpb.Entry{ent}}
+ ready = raft.Ready{CommittedEntries: []*raftpb.Entry{&ent}}
n.readyc <- ready
// "idx" applied
@@ -927,17 +838,17 @@ func TestConcurrentApplyAndSnapshotV3(t *testing.T) {
continue
}
- n.readyc <- raft.Ready{Messages: []raftpb.Message{{Type: raftpb.MsgSnap}}}
+ n.readyc <- raft.Ready{Messages: []*raftpb.Message{{Type: raftpb.MsgSnap.Enum()}}}
// get the snapshot sent by the transport
snapMsg := <-snapDoneC
// If the snapshot trails applied records, recovery will panic
// since there's no allocated snapshot at the place of the
// snapshot record. This only happens when the applier and the
// snapshot sender get out of sync.
- if snapMsg.Snapshot.Metadata.Index == idx {
+ if snapMsg.Snapshot.Metadata.GetIndex() == idx {
idx++
- snapMsg.Snapshot.Metadata.Index = idx
- ready = raft.Ready{Snapshot: *snapMsg.Snapshot}
+ snapMsg.Snapshot.Metadata.Index = new(idx)
+ ready = raft.Ready{Snapshot: snapMsg.Snapshot}
n.readyc <- ready
accepted++
} else {
@@ -961,8 +872,6 @@ func TestAddMember(t *testing.T) {
SoftState: &raft.SoftState{RaftState: raft.StateLeader},
}
cl := newTestCluster(t)
- st := v2store.New()
- cl.SetStore(st)
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -978,16 +887,14 @@ func TestAddMember(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: lg,
r: *r,
- v2store: st,
cluster: cl,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
consistIndex: cindex.NewFakeConsistentIndex(0),
beHooks: serverstorage.NewBackendHooks(lg, nil),
}
s.start()
m := membership.Member{ID: 1234, RaftAttributes: membership.RaftAttributes{PeerURLs: []string{"foo"}}}
- _, err := s.AddMember(context.Background(), m)
+ _, err := s.AddMember(t.Context(), m)
gaction := n.Action()
s.Stop()
@@ -1008,8 +915,6 @@ func TestAddMember(t *testing.T) {
func TestProcessIgnoreMismatchMessage(t *testing.T) {
lg := zaptest.NewLogger(t)
cl := newTestCluster(t)
- st := v2store.New()
- cl.SetStore(st)
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -1020,10 +925,10 @@ func TestProcessIgnoreMismatchMessage(t *testing.T) {
cl.AddMember(&membership.Member{ID: types.ID(3)}, true)
// r is initialized with ID 1.
r := realisticRaftNode(lg, 1, &raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: 11, // Magic number.
- Term: 11, // Magic number.
- ConfState: raftpb.ConfState{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: new(uint64(11)),
+ Term: new(uint64(11)), // Magic number.
+ ConfState: &raftpb.ConfState{
// Member ID list.
Voters: []uint64{1, 2, 3},
},
@@ -1035,25 +940,23 @@ func TestProcessIgnoreMismatchMessage(t *testing.T) {
lg: lg,
memberID: 1,
r: *r,
- v2store: st,
cluster: cl,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
consistIndex: cindex.NewFakeConsistentIndex(0),
beHooks: serverstorage.NewBackendHooks(lg, nil),
}
// Mock a mad switch dispatching messages to wrong node.
m := raftpb.Message{
- Type: raftpb.MsgHeartbeat,
- To: 2, // Wrong ID, s.MemberID() is 1.
- From: 3,
- Term: 11,
- Commit: 42, // Commit is larger than the last index 11.
+ Type: raftpb.MsgHeartbeat.Enum(),
+ To: new(uint64(2)), // Wrong ID, s.MemberID() is 1.
+ From: new(uint64(3)),
+ Term: new(uint64(11)),
+ Commit: new(uint64(42)), // Commit is larger than the last index 11.
}
- if types.ID(m.To) == s.MemberID() {
- t.Fatalf("m.To (%d) is expected to mismatch s.MemberID (%d)", m.To, s.MemberID())
+ if types.ID(m.GetTo()) == s.MemberID() {
+ t.Fatalf("m.To (%d) is expected to mismatch s.MemberID (%d)", m.GetTo(), s.MemberID())
}
- err := s.Process(context.Background(), m)
+ err := s.Process(t.Context(), &m)
if err == nil {
t.Fatalf("Must ignore the message and return an error")
}
@@ -1067,8 +970,6 @@ func TestRemoveMember(t *testing.T) {
SoftState: &raft.SoftState{RaftState: raft.StateLeader},
}
cl := newTestCluster(t)
- st := v2store.New()
- cl.SetStore(v2store.New())
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
@@ -1085,15 +986,13 @@ func TestRemoveMember(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
r: *r,
- v2store: st,
cluster: cl,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
consistIndex: cindex.NewFakeConsistentIndex(0),
beHooks: serverstorage.NewBackendHooks(lg, nil),
}
s.start()
- _, err := s.RemoveMember(context.Background(), 1234)
+ _, err := s.RemoveMember(t.Context(), 1234)
gaction := n.Action()
s.Stop()
@@ -1119,8 +1018,6 @@ func TestUpdateMember(t *testing.T) {
SoftState: &raft.SoftState{RaftState: raft.StateLeader},
}
cl := newTestCluster(t)
- st := v2store.New()
- cl.SetStore(st)
cl.SetBackend(schema.NewMembershipBackend(lg, be))
cl.AddMember(&membership.Member{ID: 1234}, true)
r := newRaftNode(raftNodeConfig{
@@ -1134,16 +1031,14 @@ func TestUpdateMember(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: lg,
r: *r,
- v2store: st,
cluster: cl,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
consistIndex: cindex.NewFakeConsistentIndex(0),
beHooks: serverstorage.NewBackendHooks(lg, nil),
}
s.start()
wm := membership.Member{ID: 1234, RaftAttributes: membership.RaftAttributes{PeerURLs: []string{"http://127.0.0.1:1"}}}
- _, err := s.UpdateMember(context.Background(), wm)
+ _, err := s.UpdateMember(t.Context(), wm)
gaction := n.Action()
s.Stop()
@@ -1167,7 +1062,7 @@ func TestPublishV3(t *testing.T) {
// simulate that request has gone through consensus
ch <- &apply2.Result{}
w := wait.NewWithResponse(ch)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
lg := zaptest.NewLogger(t)
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
@@ -1175,14 +1070,13 @@ func TestPublishV3(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: lg,
readych: make(chan struct{}),
- Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000},
+ Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000, ServerFeatureGate: features.NewDefaultServerFeatureGate("test", lg)},
memberID: 1,
- r: *newRaftNode(raftNodeConfig{lg: lg, Node: n}),
+ r: *newRaftNode(raftNodeConfig{lg: lg, Node: n, storage: mockstorage.NewStorageRecorder("")}),
attributes: membership.Attributes{Name: "node1", ClientURLs: []string{"http://a", "http://b"}},
cluster: &membership.RaftCluster{},
w: w,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
authStore: auth.NewAuthStore(lg, schema.NewAuthBackend(lg, be), nil, 0),
be: be,
ctx: ctx,
@@ -1199,7 +1093,7 @@ func TestPublishV3(t *testing.T) {
}
data := action[0].Params[0].([]byte)
var r pb.InternalRaftRequest
- if err := r.Unmarshal(data); err != nil {
+ if err := proto.Unmarshal(data, &r); err != nil {
t.Fatalf("unmarshal request error: %v", err)
}
assert.Equal(t, &membershippb.ClusterMemberAttrSetRequest{Member_ID: 0x1, MemberAttributes: &membershippb.Attributes{
@@ -1209,24 +1103,24 @@ func TestPublishV3(t *testing.T) {
// TestPublishV3Stopped tests that publish will be stopped if server is stopped.
func TestPublishV3Stopped(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
r := newRaftNode(raftNodeConfig{
lg: zaptest.NewLogger(t),
Node: newNodeNop(),
+ storage: mockstorage.NewStorageRecorder(""),
transport: newNopTransporter(),
})
srv := &EtcdServer{
- lgMu: new(sync.RWMutex),
- lg: zaptest.NewLogger(t),
- Cfg: config.ServerConfig{Logger: zaptest.NewLogger(t), TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries},
- r: *r,
- cluster: &membership.RaftCluster{},
- w: mockwait.NewNop(),
- done: make(chan struct{}),
- stopping: make(chan struct{}),
- stop: make(chan struct{}),
- reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
+ lgMu: new(sync.RWMutex),
+ lg: zaptest.NewLogger(t),
+ Cfg: config.ServerConfig{Logger: zaptest.NewLogger(t), TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, ServerFeatureGate: features.NewDefaultServerFeatureGate("test", nil)},
+ r: *r,
+ cluster: &membership.RaftCluster{},
+ w: mockwait.NewNop(),
+ done: make(chan struct{}),
+ stopping: make(chan struct{}),
+ stop: make(chan struct{}),
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
ctx: ctx,
cancel: cancel,
@@ -1237,7 +1131,7 @@ func TestPublishV3Stopped(t *testing.T) {
// TestPublishV3Retry tests that publish will keep retry until success.
func TestPublishV3Retry(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
n := newNodeRecorderStream()
lg := zaptest.NewLogger(t)
@@ -1247,15 +1141,14 @@ func TestPublishV3Retry(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: lg,
readych: make(chan struct{}),
- Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000},
+ Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000, ServerFeatureGate: features.NewDefaultServerFeatureGate("test", lg)},
memberID: 1,
- r: *newRaftNode(raftNodeConfig{lg: lg, Node: n}),
+ r: *newRaftNode(raftNodeConfig{lg: lg, Node: n, storage: mockstorage.NewStorageRecorder("")}),
w: mockwait.NewNop(),
stopping: make(chan struct{}),
attributes: membership.Attributes{Name: "node1", ClientURLs: []string{"http://a", "http://b"}},
cluster: &membership.RaftCluster{},
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
authStore: auth.NewAuthStore(lg, schema.NewAuthBackend(lg, be), nil, 0),
be: be,
ctx: ctx,
@@ -1291,7 +1184,7 @@ func TestUpdateVersionV3(t *testing.T) {
// simulate that request has gone through consensus
ch <- &apply2.Result{}
w := wait.NewWithResponse(ch)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
lg := zaptest.NewLogger(t)
be, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, be)
@@ -1299,13 +1192,12 @@ func TestUpdateVersionV3(t *testing.T) {
lgMu: new(sync.RWMutex),
lg: zaptest.NewLogger(t),
memberID: 1,
- Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000},
- r: *newRaftNode(raftNodeConfig{lg: zaptest.NewLogger(t), Node: n}),
+ Cfg: config.ServerConfig{Logger: lg, TickMs: 1, SnapshotCatchUpEntries: DefaultSnapshotCatchUpEntries, MaxRequestBytes: 1000, ServerFeatureGate: features.NewDefaultServerFeatureGate("test", lg)},
+ r: *newRaftNode(raftNodeConfig{lg: zaptest.NewLogger(t), Node: n, storage: mockstorage.NewStorageRecorder("")}),
attributes: membership.Attributes{Name: "node1", ClientURLs: []string{"http://node1.com"}},
cluster: &membership.RaftCluster{},
w: w,
reqIDGen: idutil.NewGenerator(0, time.Time{}),
- SyncTicker: &time.Ticker{},
authStore: auth.NewAuthStore(lg, schema.NewAuthBackend(lg, be), nil, 0),
be: be,
@@ -1324,7 +1216,7 @@ func TestUpdateVersionV3(t *testing.T) {
}
data := action[0].Params[0].([]byte)
var r pb.InternalRaftRequest
- if err := r.Unmarshal(data); err != nil {
+ if err := proto.Unmarshal(data, &r); err != nil {
t.Fatalf("unmarshal request error: %v", err)
}
assert.Equal(t, &membershippb.ClusterVersionSetRequest{Ver: ver}, r.ClusterVersionSet)
@@ -1417,7 +1309,7 @@ func (n *nodeRecorder) ProposeConfChange(ctx context.Context, conf raftpb.ConfCh
return nil
}
-func (n *nodeRecorder) Step(ctx context.Context, msg raftpb.Message) error {
+func (n *nodeRecorder) Step(ctx context.Context, msg *raftpb.Message) error {
n.Record(testutil.Action{Name: "Step"})
return nil
}
@@ -1502,7 +1394,7 @@ func (n *nodeConfChangeCommitterRecorder) ProposeConfChange(ctx context.Context,
n.index++
n.Record(testutil.Action{Name: "ProposeConfChange:" + confChangeActionName(conf)})
- n.readyc <- raft.Ready{CommittedEntries: []raftpb.Entry{{Index: n.index, Type: typ, Data: data}}}
+ n.readyc <- raft.Ready{CommittedEntries: []*raftpb.Entry{{Index: new(n.index), Type: &typ, Data: data}}}
return nil
}
@@ -1515,18 +1407,8 @@ func (n *nodeConfChangeCommitterRecorder) ApplyConfChange(conf raftpb.ConfChange
return &raftpb.ConfState{}
}
-func newTestCluster(t testing.TB) *membership.RaftCluster {
- return membership.NewCluster(zaptest.NewLogger(t))
-}
-
-func newTestClusterWithBackend(t testing.TB, membs []*membership.Member, be backend.Backend) *membership.RaftCluster {
- lg := zaptest.NewLogger(t)
- c := membership.NewCluster(lg)
- c.SetBackend(schema.NewMembershipBackend(lg, be))
- for _, m := range membs {
- c.AddMember(m, true)
- }
- return c
+func newTestCluster(tb testing.TB) *membership.RaftCluster {
+ return membership.NewCluster(zaptest.NewLogger(tb))
}
type nopTransporter struct{}
@@ -1537,8 +1419,8 @@ func newNopTransporter() rafthttp.Transporter {
func (s *nopTransporter) Start() error { return nil }
func (s *nopTransporter) Handler() http.Handler { return nil }
-func (s *nopTransporter) Send(m []raftpb.Message) {}
-func (s *nopTransporter) SendSnapshot(m snap.Message) {}
+func (s *nopTransporter) Send(m []*raftpb.Message) {}
+func (s *nopTransporter) SendSnapshot(m *snap.Message) {}
func (s *nopTransporter) AddRemote(id types.ID, us []string) {}
func (s *nopTransporter) AddPeer(id types.ID, us []string) {}
func (s *nopTransporter) RemovePeer(id types.ID) {}
@@ -1552,20 +1434,20 @@ func (s *nopTransporter) Resume() {}
type snapTransporter struct {
nopTransporter
- snapDoneC chan snap.Message
+ snapDoneC chan *snap.Message
snapDir string
lg *zap.Logger
}
-func newSnapTransporter(lg *zap.Logger, snapDir string) (rafthttp.Transporter, <-chan snap.Message) {
- ch := make(chan snap.Message, 1)
+func newSnapTransporter(lg *zap.Logger, snapDir string) (rafthttp.Transporter, <-chan *snap.Message) {
+ ch := make(chan *snap.Message, 1)
tr := &snapTransporter{snapDoneC: ch, snapDir: snapDir, lg: lg}
return tr, ch
}
-func (s *snapTransporter) SendSnapshot(m snap.Message) {
+func (s *snapTransporter) SendSnapshot(m *snap.Message) {
ss := snap.New(s.lg, s.snapDir)
- ss.SaveDBFrom(m.ReadCloser, m.Snapshot.Metadata.Index+1)
+ ss.SaveDBFrom(m.ReadCloser, m.Snapshot.Metadata.GetIndex()+1)
m.CloseWithError(nil)
s.snapDoneC <- m
}
@@ -1581,10 +1463,10 @@ func newSendMsgAppRespTransporter() (rafthttp.Transporter, <-chan int) {
return tr, ch
}
-func (s *sendMsgAppRespTransporter) Send(m []raftpb.Message) {
+func (s *sendMsgAppRespTransporter) Send(m []*raftpb.Message) {
var send int
for _, msg := range m {
- if msg.To != 0 {
+ if msg.GetTo() != 0 {
send++
}
}
@@ -1628,12 +1510,11 @@ func TestWaitAppliedIndex(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
s := &EtcdServer{
- appliedIndex: tc.appliedIndex,
- committedIndex: tc.committedIndex,
- stopping: make(chan struct{}, 1),
- applyWait: wait.NewTimeList(),
+ stopping: make(chan struct{}, 1),
+ applyWait: wait.NewTimeList(),
}
-
+ s.appliedIndex.Store(tc.appliedIndex)
+ s.committedIndex.Store(tc.committedIndex)
if tc.action != nil {
go tc.action(s)
}
@@ -1715,198 +1596,3 @@ func TestAddFeatureGateMetrics(t *testing.T) {
err := ptestutil.GatherAndCompare(prometheus.DefaultGatherer, strings.NewReader(expected), "etcd_server_feature_enabled")
require.NoErrorf(t, err, "unexpected metric collection result: \n%s", err)
}
-
-func TestRequestCurrentIndex_LeaderChangedRace(t *testing.T) {
- s, _ := setupTestRequestCurrentIndex(t)
-
- for i := 0; i < 100; i++ {
- s.r.readStateC <- raft.ReadState{Index: 100}
- leaderChangedNotifier := s.leaderChanged.Receive()
- s.leaderChanged.Notify()
-
- index, err := s.requestCurrentIndex(leaderChangedNotifier)
- require.ErrorIs(t, err, errors.ErrLeaderChanged)
- require.Equal(t, uint64(0), index)
-
- // Clear the readStateC channel for the next iteration,
- select {
- case <-s.r.readStateC:
- default:
- }
- }
-}
-
-func TestRequestCurrentIndex_UniqueRequestID(t *testing.T) {
- s, mockRaft := setupTestRequestCurrentIndex(t)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- go func() {
- defer wg.Done()
- s.requestCurrentIndex(s.leaderChanged.Receive())
- }()
-
- require.Eventually(t, func() bool {
- return len(mockRaft.getRequests()) >= 2
- }, time.Second, 100*time.Millisecond)
-
- s.leaderChanged.Notify()
- wg.Wait()
-
- seen := make(map[uint64]bool)
- for _, id := range mockRaft.getRequests() {
- require.Falsef(t, seen[id], "Found duplicate request ID: %d", id)
- seen[id] = true
- }
-}
-
-func TestRequestCurrentIndex_Success(t *testing.T) {
- s, mockRaft := setupTestRequestCurrentIndex(t)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- var index uint64
- var err error
- go func() {
- defer wg.Done()
- index, err = s.requestCurrentIndex(s.leaderChanged.Receive())
- }()
-
- require.Eventually(t, func() bool {
- return len(mockRaft.getRequests()) == 1
- }, time.Second, 100*time.Millisecond)
-
- reqID := mockRaft.getRequests()[0]
- reqIDBytes := make([]byte, 8)
- binary.BigEndian.PutUint64(reqIDBytes, reqID)
-
- s.r.readStateC <- raft.ReadState{
- Index: 100,
- RequestCtx: reqIDBytes,
- }
-
- wg.Wait()
-
- require.NoError(t, err)
- require.Equal(t, uint64(100), index)
- require.Lenf(t, mockRaft.getRequests(), 1, "Expected exactly 1 ReadIndex request")
-}
-
-func TestRequestCurrentIndex_WrongRequestID(t *testing.T) {
- s, mockRaft := setupTestRequestCurrentIndex(t)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- var index uint64
- var err error
- go func() {
- defer wg.Done()
- index, err = s.requestCurrentIndex(s.leaderChanged.Receive())
- }()
-
- require.Eventually(t, func() bool {
- return len(mockRaft.getRequests()) == 1
- }, time.Second, 10*time.Millisecond)
-
- wrongReqIDBytes := make([]byte, 8)
- binary.BigEndian.PutUint64(wrongReqIDBytes, 99999)
-
- s.r.readStateC <- raft.ReadState{
- Index: 100,
- RequestCtx: wrongReqIDBytes,
- }
-
- time.Sleep(100 * time.Millisecond)
- requests := mockRaft.getRequests()
- require.Lenf(t, requests, 1, "Expected exactly 1 ReadIndex request")
-
- reqID := requests[0]
- reqIDBytes := make([]byte, 8)
- binary.BigEndian.PutUint64(reqIDBytes, reqID)
-
- s.r.readStateC <- raft.ReadState{
- Index: 99,
- RequestCtx: reqIDBytes,
- }
- wg.Wait()
-
- require.NoError(t, err)
- require.Equal(t, uint64(99), index)
- require.Lenf(t, mockRaft.getRequests(), 1, "Expected exactly 1 ReadIndex request")
-}
-
-func TestRequestCurrentIndex_DelayedResponse(t *testing.T) {
- s, mockRaft := setupTestRequestCurrentIndex(t)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- var index uint64
- var err error
- go func() {
- defer wg.Done()
- index, err = s.requestCurrentIndex(s.leaderChanged.Receive())
- }()
-
- require.Eventually(t, func() bool {
- return len(mockRaft.getRequests()) >= 3
- }, 2*time.Second, 100*time.Millisecond)
- requests := mockRaft.getRequests()
-
- reqID := requests[1]
- reqIDBytes := make([]byte, 8)
- binary.BigEndian.PutUint64(reqIDBytes, reqID)
-
- select {
- case s.r.readStateC <- raft.ReadState{
- Index: 100,
- RequestCtx: reqIDBytes,
- }:
- case <-time.After(time.Second):
- t.Fatal("timed out sending read state")
- }
- wg.Wait()
-
- require.NoError(t, err)
- require.Equal(t, uint64(100), index)
-}
-
-func setupTestRequestCurrentIndex(t *testing.T) (*EtcdServer, *testRaftNode) {
- mockRaft := &testRaftNode{}
- s := &EtcdServer{
- lgMu: new(sync.RWMutex),
- lg: zaptest.NewLogger(t),
- reqIDGen: idutil.NewGenerator(0, time.Time{}),
- firstCommitInTerm: notify.NewNotifier(),
- leaderChanged: notify.NewNotifier(),
- r: raftNode{
- raftNodeConfig: raftNodeConfig{
- Node: mockRaft,
- },
- readStateC: make(chan raft.ReadState, 1),
- },
- }
- return s, mockRaft
-}
-
-type testRaftNode struct {
- raft.Node
- mu sync.Mutex
- readIndexRequests []uint64
-}
-
-func (m *testRaftNode) ReadIndex(ctx context.Context, rctx []byte) error {
- m.mu.Lock()
- defer m.mu.Unlock()
- if len(rctx) == 8 {
- m.readIndexRequests = append(m.readIndexRequests, binary.BigEndian.Uint64(rctx))
- }
- return nil
-}
-
-func (m *testRaftNode) getRequests() []uint64 {
- m.mu.Lock()
- defer m.mu.Unlock()
- res := make([]uint64, len(m.readIndexRequests))
- copy(res, m.readIndexRequests)
- return res
-}
diff --git a/server/etcdserver/snapshot_merge.go b/server/etcdserver/snapshot_merge.go
index cc3c545bee2a..7f01f13de9f4 100644
--- a/server/etcdserver/snapshot_merge.go
+++ b/server/etcdserver/snapshot_merge.go
@@ -19,6 +19,7 @@ import (
humanize "github.com/dustin/go-humanize"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -28,7 +29,7 @@ import (
// createMergedSnapshotMessage creates a snapshot message that contains: raft status (term, conf),
// a snapshot of v2 store inside raft.Snapshot as []byte, a snapshot of v3 KV in the top level message
// as ReadCloser.
-func (s *EtcdServer) createMergedSnapshotMessage(m raftpb.Message, snapt, snapi uint64, confState raftpb.ConfState) snap.Message {
+func (s *EtcdServer) createMergedSnapshotMessage(m *raftpb.Message, snapt, snapi uint64, confState *raftpb.ConfState) *snap.Message {
lg := s.Logger()
// get a snapshot of v2 store as []byte
d := GetMembershipInfoInV2Format(lg, s.cluster)
@@ -41,19 +42,20 @@ func (s *EtcdServer) createMergedSnapshotMessage(m raftpb.Message, snapt, snapi
// put the []byte snapshot of store into raft snapshot and return the merged snapshot with
// KV readCloser snapshot.
- snapshot := raftpb.Snapshot{
- Metadata: raftpb.SnapshotMetadata{
- Index: snapi,
- Term: snapt,
- ConfState: confState,
+ snapshot := &raftpb.Snapshot{
+ Metadata: &raftpb.SnapshotMetadata{
+ Index: &snapi,
+ Term: &snapt,
+ // Defensive copy as sending snapshot is async
+ ConfState: proto.Clone(confState).(*raftpb.ConfState),
},
Data: d,
}
- m.Snapshot = &snapshot
+ m.Snapshot = snapshot
verifySnapshotIndex(snapshot, s.consistIndex.ConsistentIndex())
- return *snap.NewMessage(m, rc, dbsnap.Size())
+ return snap.NewMessage(m, rc, dbsnap.Size())
}
func newSnapshotReaderCloser(lg *zap.Logger, snapshot backend.Snapshot) io.ReadCloser {
diff --git a/server/etcdserver/tracing.go b/server/etcdserver/tracing.go
new file mode 100644
index 000000000000..6050500e04ea
--- /dev/null
+++ b/server/etcdserver/tracing.go
@@ -0,0 +1,74 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 etcdserver
+
+import pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+
+// firstCompareKey returns first non-empty key in the list of comparison operations.
+func firstCompareKey(c []*pb.Compare) string {
+ for _, op := range c {
+ key := string(op.GetKey())
+ if key != "" {
+ return key
+ }
+ }
+ return ""
+}
+
+// firstOpKey returns first non-empty key in the list of request operations.
+func firstOpKey(ops []*pb.RequestOp) string {
+ for _, operation := range ops {
+ var key string
+ switch op := operation.GetRequest().(type) {
+ case *pb.RequestOp_RequestPut:
+ key = string(op.RequestPut.GetKey())
+ case *pb.RequestOp_RequestRange:
+ key = string(op.RequestRange.GetKey())
+ case *pb.RequestOp_RequestDeleteRange:
+ key = string(op.RequestDeleteRange.GetKey())
+ }
+ if key != "" {
+ return key
+ }
+ }
+ return ""
+}
+
+// firstOpType returns type of the first operation in the list.
+func firstOpType(ops []*pb.RequestOp) string {
+ for _, operation := range ops {
+ switch operation.GetRequest().(type) {
+ case *pb.RequestOp_RequestPut:
+ return "put"
+ case *pb.RequestOp_RequestRange:
+ return "range"
+ case *pb.RequestOp_RequestDeleteRange:
+ return "delete_range"
+ case *pb.RequestOp_RequestTxn:
+ return "txn"
+ }
+ }
+ return ""
+}
+
+// firstOpLease returns lease ID of the first PUT operation in the list.
+func firstOpLease(ops []*pb.RequestOp) int64 {
+ for _, operation := range ops {
+ if op, ok := operation.GetRequest().(*pb.RequestOp_RequestPut); ok {
+ return op.RequestPut.GetLease()
+ }
+ }
+ return -1
+}
diff --git a/server/etcdserver/txn/delete.go b/server/etcdserver/txn/delete.go
new file mode 100644
index 000000000000..20d1c68feabb
--- /dev/null
+++ b/server/etcdserver/txn/delete.go
@@ -0,0 +1,68 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 txn
+
+import (
+ "context"
+
+ "go.uber.org/zap"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+func DeleteRange(ctx context.Context, lg *zap.Logger, kv mvcc.KV, dr *pb.DeleteRangeRequest) (resp *pb.DeleteRangeResponse, trace *traceutil.Trace, err error) {
+ ctx, trace = traceutil.EnsureTrace(ctx, lg, "delete_range",
+ traceutil.Field{Key: "key", Value: string(dr.Key)},
+ traceutil.Field{Key: "range_end", Value: string(dr.RangeEnd)},
+ )
+ txnWrite := kv.Write(trace)
+ defer txnWrite.End()
+ resp, err = deleteRange(ctx, txnWrite, dr)
+ return resp, trace, err
+}
+
+func deleteRange(ctx context.Context, txnWrite mvcc.TxnWrite, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
+ resp := &pb.DeleteRangeResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ end := mkGteRange(dr.RangeEnd)
+
+ if dr.PrevKv {
+ rr, err := txnWrite.Range(ctx, dr.Key, end, mvcc.RangeOptions{})
+ if err != nil {
+ return nil, err
+ }
+ if rr != nil {
+ resp.PrevKvs = make([]*mvccpb.KeyValue, len(rr.KVs))
+ copy(resp.PrevKvs, rr.KVs)
+ }
+ }
+
+ resp.Deleted, resp.Header.Revision = txnWrite.DeleteRange(dr.Key, end)
+ return resp, nil
+}
+
+// mkGteRange determines if the range end is a >= range. This works around grpc
+// sending empty byte strings as nil; >= is encoded in the range end as '\0'.
+// If it is a GTE range, then []byte{} is returned to indicate the empty byte
+// string (vs nil being no byte string).
+func mkGteRange(rangeEnd []byte) []byte {
+ if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
+ return []byte{}
+ }
+ return rangeEnd
+}
diff --git a/server/etcdserver/txn/put.go b/server/etcdserver/txn/put.go
new file mode 100644
index 000000000000..ec3ae38c7b8e
--- /dev/null
+++ b/server/etcdserver/txn/put.go
@@ -0,0 +1,115 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 txn
+
+import (
+ "context"
+
+ "go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/etcdserver/errors"
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+func Put(ctx context.Context, lg *zap.Logger, lessor lease.Lessor, kv mvcc.KV, p *pb.PutRequest) (resp *pb.PutResponse, trace *traceutil.Trace, err error) {
+ ctx, trace = traceutil.EnsureTrace(ctx, lg, "put",
+ traceutil.Field{Key: "key", Value: string(p.Key)},
+ traceutil.Field{Key: "req_size", Value: proto.Size(p)},
+ )
+ err = checkLease(lessor, p)
+ if err != nil {
+ return nil, trace, err
+ }
+ txnWrite := kv.Write(trace)
+ defer txnWrite.End()
+ prevKV, err := checkAndGetPrevKV(trace, txnWrite, p)
+ if err != nil {
+ return nil, trace, err
+ }
+ return put(ctx, txnWrite, p, prevKV), trace, nil
+}
+
+func put(ctx context.Context, txnWrite mvcc.TxnWrite, p *pb.PutRequest, prevKV *mvcc.RangeResult) *pb.PutResponse {
+ trace := traceutil.Get(ctx)
+ resp := &pb.PutResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ val, leaseID := p.Value, lease.LeaseID(p.Lease)
+
+ if p.IgnoreValue {
+ val = prevKV.KVs[0].Value
+ }
+ if p.IgnoreLease {
+ leaseID = lease.LeaseID(prevKV.KVs[0].Lease)
+ }
+ if p.PrevKv {
+ if prevKV != nil && len(prevKV.KVs) != 0 {
+ resp.PrevKv = prevKV.KVs[0]
+ }
+ }
+
+ resp.Header.Revision = txnWrite.Put(p.Key, val, leaseID)
+ trace.AddField(traceutil.Field{Key: "response_revision", Value: resp.Header.Revision})
+ return resp
+}
+
+func checkPut(trace *traceutil.Trace, txnWrite mvcc.ReadView, lessor lease.Lessor, p *pb.PutRequest) error {
+ err := checkLease(lessor, p)
+ if err != nil {
+ return err
+ }
+ _, err = checkAndGetPrevKV(trace, txnWrite, p)
+ return err
+}
+
+func checkLease(lessor lease.Lessor, p *pb.PutRequest) error {
+ leaseID := lease.LeaseID(p.Lease)
+ if leaseID != lease.NoLease {
+ if l := lessor.Lookup(leaseID); l == nil {
+ return lease.ErrLeaseNotFound
+ }
+ }
+ return nil
+}
+
+func checkAndGetPrevKV(trace *traceutil.Trace, txnWrite mvcc.ReadView, p *pb.PutRequest) (prevKV *mvcc.RangeResult, err error) {
+ prevKV, err = getPrevKV(trace, txnWrite, p)
+ if err != nil {
+ return nil, err
+ }
+ if p.IgnoreValue || p.IgnoreLease {
+ if prevKV == nil || len(prevKV.KVs) == 0 {
+ // ignore_{lease,value} flag expects previous key-value pair
+ return nil, errors.ErrKeyNotFound
+ }
+ }
+ return prevKV, nil
+}
+
+func getPrevKV(trace *traceutil.Trace, txnWrite mvcc.ReadView, p *pb.PutRequest) (prevKV *mvcc.RangeResult, err error) {
+ if p.IgnoreValue || p.IgnoreLease || p.PrevKv {
+ trace.StepWithFunction(func() {
+ prevKV, err = txnWrite.Range(context.TODO(), p.Key, nil, mvcc.RangeOptions{})
+ }, "get previous kv pair")
+
+ if err != nil {
+ return nil, err
+ }
+ }
+ return prevKV, nil
+}
diff --git a/server/etcdserver/txn/range.go b/server/etcdserver/txn/range.go
new file mode 100644
index 000000000000..5331611ea0c2
--- /dev/null
+++ b/server/etcdserver/txn/range.go
@@ -0,0 +1,234 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 txn
+
+import (
+ "bytes"
+ "context"
+ "math"
+ "sort"
+ "time"
+
+ "go.uber.org/zap"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ "go.etcd.io/etcd/pkg/v3/traceutil"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+// Count returns the number of keys in [key, rangeEnd) at the given revision.
+// A revision of 0 reads at the latest revision.
+func Count(ctx context.Context, lg *zap.Logger, kv mvcc.KV, key, rangeEnd []byte, revision int64) (int64, error) {
+ resp, _, err := Range(ctx, lg, kv, &pb.RangeRequest{
+ Key: key,
+ RangeEnd: rangeEnd,
+ Revision: revision,
+ CountOnly: true,
+ }, false)
+ if err != nil {
+ return 0, err
+ }
+ return resp.Count, nil
+}
+
+func Range(ctx context.Context, lg *zap.Logger, kv mvcc.KV, r *pb.RangeRequest, withTotalCount bool) (resp *pb.RangeResponse, trace *traceutil.Trace, err error) {
+ ctx, trace = traceutil.EnsureTrace(ctx, lg, "range")
+ defer func(start time.Time) {
+ success := err == nil
+ RangeSecObserve(success, time.Since(start))
+ }(time.Now())
+ txnRead := kv.Read(mvcc.ConcurrentReadTxMode, trace)
+ defer txnRead.End()
+ resp, err = executeRange(ctx, lg, txnRead, r, withTotalCount)
+ return resp, trace, err
+}
+
+func executeRange(ctx context.Context, lg *zap.Logger, txnRead mvcc.TxnRead, r *pb.RangeRequest, withTotalCount bool) (*pb.RangeResponse, error) {
+ trace := traceutil.Get(ctx)
+
+ limit := rangeLimit(r)
+ ro := mvcc.RangeOptions{
+ Limit: limit,
+ Rev: r.Revision,
+ CountOnly: r.CountOnly,
+ FastKeysOnly: r.KeysOnly && r.SortTarget != pb.RangeRequest_VALUE,
+ WithTotalCount: withTotalCount,
+ }
+
+ rr, err := txnRead.Range(ctx, r.Key, mkGteRange(r.RangeEnd), ro)
+ if err != nil {
+ return nil, err
+ }
+
+ filterRangeResults(rr, r)
+ sortRangeResults(rr, r, lg)
+ trace.Step("filter and sort the key-value pairs")
+
+ resp := asembleRangeResponse(rr, r)
+ trace.Step("assemble the response")
+
+ return resp, nil
+}
+
+func rangeLimit(r *pb.RangeRequest) int64 {
+ limit := r.Limit
+ if !IsDefaultOrdering(r.SortTarget, r.SortOrder) || HasRevisionFilters(r) {
+ limit = 0
+ }
+ if limit > 0 && limit < math.MaxInt64 {
+ limit = limit + 1
+ }
+ return limit
+}
+
+func IsDefaultOrdering(sortTarget pb.RangeRequest_SortTarget, sortOrder pb.RangeRequest_SortOrder) bool {
+ // Since current mvcc.Range implementation returns results
+ // sorted by keys in lexiographically ascending order,
+ // don't re-sort when target is 'KEY' and order is ASCEND
+ return sortOrder == pb.RangeRequest_NONE ||
+ (sortTarget == pb.RangeRequest_KEY && sortOrder == pb.RangeRequest_ASCEND)
+}
+
+func HasRevisionFilters(r *pb.RangeRequest) bool {
+ return r.MinModRevision != 0 || r.MaxModRevision != 0 ||
+ r.MinCreateRevision != 0 || r.MaxCreateRevision != 0
+}
+
+func filterRangeResults(rr *mvcc.RangeResult, r *pb.RangeRequest) {
+ if r.MaxModRevision != 0 {
+ pruneKVs(rr, func(kv *mvccpb.KeyValue) bool { return kv.ModRevision > r.MaxModRevision })
+ }
+ if r.MinModRevision != 0 {
+ pruneKVs(rr, func(kv *mvccpb.KeyValue) bool { return kv.ModRevision < r.MinModRevision })
+ }
+ if r.MaxCreateRevision != 0 {
+ pruneKVs(rr, func(kv *mvccpb.KeyValue) bool { return kv.CreateRevision > r.MaxCreateRevision })
+ }
+ if r.MinCreateRevision != 0 {
+ pruneKVs(rr, func(kv *mvccpb.KeyValue) bool { return kv.CreateRevision < r.MinCreateRevision })
+ }
+}
+
+func sortRangeResults(rr *mvcc.RangeResult, r *pb.RangeRequest, lg *zap.Logger) {
+ sortOrder := r.SortOrder
+ if r.SortTarget != pb.RangeRequest_KEY && sortOrder == pb.RangeRequest_NONE {
+ sortOrder = pb.RangeRequest_ASCEND
+ }
+
+ if !IsDefaultOrdering(r.SortTarget, sortOrder) {
+ var sorter sort.Interface
+ switch {
+ case r.SortTarget == pb.RangeRequest_KEY:
+ sorter = &kvSortByKey{&kvSort{rr.KVs}}
+ case r.SortTarget == pb.RangeRequest_VERSION:
+ sorter = &kvSortByVersion{&kvSort{rr.KVs}}
+ case r.SortTarget == pb.RangeRequest_CREATE:
+ sorter = &kvSortByCreate{&kvSort{rr.KVs}}
+ case r.SortTarget == pb.RangeRequest_MOD:
+ sorter = &kvSortByMod{&kvSort{rr.KVs}}
+ case r.SortTarget == pb.RangeRequest_VALUE:
+ sorter = &kvSortByValue{&kvSort{rr.KVs}}
+ default:
+ lg.Panic("unexpected sort target", zap.Int32("sort-target", int32(r.SortTarget)))
+ }
+ switch {
+ case sortOrder == pb.RangeRequest_ASCEND:
+ sort.Sort(sorter)
+ case sortOrder == pb.RangeRequest_DESCEND:
+ sort.Sort(sort.Reverse(sorter))
+ }
+ }
+}
+
+func asembleRangeResponse(rr *mvcc.RangeResult, r *pb.RangeRequest) *pb.RangeResponse {
+ resp := &pb.RangeResponse{Header: &pb.ResponseHeader{}}
+ if r.Limit > 0 && len(rr.KVs) > int(r.Limit) {
+ rr.KVs = rr.KVs[:r.Limit]
+ resp.More = true
+ }
+ resp.Header.Revision = rr.Rev
+ resp.Count = int64(rr.Count)
+ resp.Kvs = make([]*mvccpb.KeyValue, len(rr.KVs))
+ for i := range rr.KVs {
+ if r.KeysOnly {
+ rr.KVs[i].Value = nil
+ }
+ resp.Kvs[i] = rr.KVs[i]
+ }
+ return resp
+}
+
+func checkRange(rv mvcc.ReadView, req *pb.RangeRequest) error {
+ switch {
+ case req.Revision == 0:
+ return nil
+ case req.Revision > rv.Rev():
+ return mvcc.ErrFutureRev
+ case req.Revision < rv.FirstRev():
+ return mvcc.ErrCompacted
+ }
+ return nil
+}
+
+func pruneKVs(rr *mvcc.RangeResult, isPrunable func(*mvccpb.KeyValue) bool) {
+ j := 0
+ for i := range rr.KVs {
+ rr.KVs[j] = rr.KVs[i]
+ if !isPrunable(rr.KVs[i]) {
+ j++
+ }
+ }
+ rr.KVs = rr.KVs[:j]
+}
+
+type kvSort struct{ kvs []*mvccpb.KeyValue }
+
+func (s *kvSort) Swap(i, j int) {
+ t := s.kvs[i]
+ s.kvs[i] = s.kvs[j]
+ s.kvs[j] = t
+}
+func (s *kvSort) Len() int { return len(s.kvs) }
+
+type kvSortByKey struct{ *kvSort }
+
+func (s *kvSortByKey) Less(i, j int) bool {
+ return bytes.Compare(s.kvs[i].Key, s.kvs[j].Key) < 0
+}
+
+type kvSortByVersion struct{ *kvSort }
+
+func (s *kvSortByVersion) Less(i, j int) bool {
+ return (s.kvs[i].Version - s.kvs[j].Version) < 0
+}
+
+type kvSortByCreate struct{ *kvSort }
+
+func (s *kvSortByCreate) Less(i, j int) bool {
+ return (s.kvs[i].CreateRevision - s.kvs[j].CreateRevision) < 0
+}
+
+type kvSortByMod struct{ *kvSort }
+
+func (s *kvSortByMod) Less(i, j int) bool {
+ return (s.kvs[i].ModRevision - s.kvs[j].ModRevision) < 0
+}
+
+type kvSortByValue struct{ *kvSort }
+
+func (s *kvSortByValue) Less(i, j int) bool {
+ return bytes.Compare(s.kvs[i].Value, s.kvs[j].Value) < 0
+}
diff --git a/server/etcdserver/txn/range_bench_test.go b/server/etcdserver/txn/range_bench_test.go
new file mode 100644
index 000000000000..4f42cc662d6e
--- /dev/null
+++ b/server/etcdserver/txn/range_bench_test.go
@@ -0,0 +1,150 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 txn
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "go.uber.org/zap"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/server/v3/lease"
+ "go.etcd.io/etcd/server/v3/storage/backend"
+ betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
+ "go.etcd.io/etcd/server/v3/storage/mvcc"
+)
+
+// BenchmarkRange benchmarks Range requests with different configurations.
+// This helps demonstrate the performance improvement from the KEY ASCEND optimization.
+func BenchmarkRange(b *testing.B) {
+ benchmarks := []struct {
+ name string
+ limit int64
+ sortOrder pb.RangeRequest_SortOrder
+ sortTarget pb.RangeRequest_SortTarget
+ }{
+ {
+ name: "no_sort_no_limit",
+ limit: 0,
+ },
+ {
+ name: "no_sort_limit_10",
+ limit: 10,
+ },
+ {
+ name: "key_ascend_limit_10",
+ limit: 10,
+ sortOrder: pb.RangeRequest_ASCEND,
+ sortTarget: pb.RangeRequest_KEY,
+ },
+ {
+ name: "key_descend_limit_10",
+ limit: 10,
+ sortOrder: pb.RangeRequest_DESCEND,
+ sortTarget: pb.RangeRequest_KEY,
+ },
+ {
+ name: "create_ascend_limit_10",
+ limit: 10,
+ sortOrder: pb.RangeRequest_ASCEND,
+ sortTarget: pb.RangeRequest_CREATE,
+ },
+ }
+
+ for _, keyCount := range []int{100, 1000, 10000} {
+ for _, bm := range benchmarks {
+ b.Run(fmt.Sprintf("keys_%d/%s", keyCount, bm.name), func(b *testing.B) {
+ // Disable bbolt logging to avoid interfering with benchmark results.
+ bcfg := backend.DefaultBackendConfig(zap.NewNop())
+ dir, _ := os.MkdirTemp(b.TempDir(), "etcd_backend_bench")
+ bcfg.Path = filepath.Join(dir, "database")
+ be := backend.New(bcfg)
+ defer betesting.Close(b, be)
+ s := mvcc.NewStore(zap.NewNop(), be, &lease.FakeLessor{}, mvcc.StoreConfig{})
+ defer s.Close()
+
+ // Insert keys
+ for i := range keyCount {
+ s.Put([]byte(fmt.Sprintf("key-%05d", i)), []byte("value"), lease.NoLease)
+ }
+ s.Commit()
+
+ ctx := context.Background()
+ req := &pb.RangeRequest{
+ Key: []byte(""),
+ RangeEnd: []byte{0},
+ Limit: bm.limit,
+ SortOrder: bm.sortOrder,
+ SortTarget: bm.sortTarget,
+ }
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for b.Loop() {
+ _, _, err := Range(ctx, zap.NewNop(), s, req, true)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ }
+ }
+}
+
+func BenchmarkRangeFastKeysOnlyWithLimit(b *testing.B) {
+ for _, keyCount := range []int{100, 1000, 10000, 100000} {
+ b.Run(fmt.Sprintf("keys_%d", keyCount), func(b *testing.B) {
+ bcfg := backend.DefaultBackendConfig(zap.NewNop())
+ dir, _ := os.MkdirTemp(b.TempDir(), "etcd_backend_bench")
+ bcfg.Path = filepath.Join(dir, "database")
+ be := backend.New(bcfg)
+ defer betesting.Close(b, be)
+ s := mvcc.NewStore(zap.NewNop(), be, &lease.FakeLessor{}, mvcc.StoreConfig{})
+ defer s.Close()
+
+ for i := range keyCount {
+ s.Put([]byte(fmt.Sprintf("key-%06d", i)), []byte("value"), lease.NoLease)
+ }
+ s.Commit()
+
+ ctx := context.Background()
+ req := &pb.RangeRequest{
+ Key: []byte(""),
+ RangeEnd: []byte{0},
+ Limit: 10,
+ KeysOnly: true,
+ SortOrder: pb.RangeRequest_NONE,
+ }
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for b.Loop() {
+ // TODO: This benchmark currently returns all keys due to the fast keys only optimization.
+ // We should update it to return the correct number of keys once the optimization is fixed.
+ resp, _, err := Range(ctx, zap.NewNop(), s, req, false /* withTotalCount */)
+ if err != nil {
+ b.Fatal(err)
+ }
+ if len(resp.Kvs) != int(req.Limit) {
+ b.Fatalf("expected %d kvs, got %d", req.Limit, len(resp.Kvs))
+ }
+ }
+ })
+ }
+}
diff --git a/server/etcdserver/txn/txn.go b/server/etcdserver/txn/txn.go
index 783cfdd151b3..693c04289bf5 100644
--- a/server/etcdserver/txn/txn.go
+++ b/server/etcdserver/txn/txn.go
@@ -18,243 +18,19 @@ import (
"bytes"
"context"
"fmt"
- "sort"
- "time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/pkg/v3/traceutil"
- "go.etcd.io/etcd/server/v3/auth"
- "go.etcd.io/etcd/server/v3/etcdserver/errors"
"go.etcd.io/etcd/server/v3/lease"
"go.etcd.io/etcd/server/v3/storage/mvcc"
)
-func Put(ctx context.Context, lg *zap.Logger, lessor lease.Lessor, kv mvcc.KV, p *pb.PutRequest) (resp *pb.PutResponse, trace *traceutil.Trace, err error) {
- trace = traceutil.Get(ctx)
- // create put tracing if the trace in context is empty
- if trace.IsEmpty() {
- trace = traceutil.New("put",
- lg,
- traceutil.Field{Key: "key", Value: string(p.Key)},
- traceutil.Field{Key: "req_size", Value: p.Size()},
- )
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
- }
- leaseID := lease.LeaseID(p.Lease)
- if leaseID != lease.NoLease {
- if l := lessor.Lookup(leaseID); l == nil {
- return nil, nil, lease.ErrLeaseNotFound
- }
- }
- txnWrite := kv.Write(trace)
- defer txnWrite.End()
- resp, err = put(ctx, txnWrite, p)
- return resp, trace, err
-}
-
-func put(ctx context.Context, txnWrite mvcc.TxnWrite, p *pb.PutRequest) (resp *pb.PutResponse, err error) {
- trace := traceutil.Get(ctx)
- resp = &pb.PutResponse{}
- resp.Header = &pb.ResponseHeader{}
- val, leaseID := p.Value, lease.LeaseID(p.Lease)
-
- var rr *mvcc.RangeResult
- if p.IgnoreValue || p.IgnoreLease || p.PrevKv {
- trace.StepWithFunction(func() {
- rr, err = txnWrite.Range(context.TODO(), p.Key, nil, mvcc.RangeOptions{})
- }, "get previous kv pair")
-
- if err != nil {
- return nil, err
- }
- }
- if p.IgnoreValue || p.IgnoreLease {
- if rr == nil || len(rr.KVs) == 0 {
- // ignore_{lease,value} flag expects previous key-value pair
- return nil, errors.ErrKeyNotFound
- }
- }
- if p.IgnoreValue {
- val = rr.KVs[0].Value
- }
- if p.IgnoreLease {
- leaseID = lease.LeaseID(rr.KVs[0].Lease)
- }
- if p.PrevKv {
- if rr != nil && len(rr.KVs) != 0 {
- resp.PrevKv = &rr.KVs[0]
- }
- }
-
- resp.Header.Revision = txnWrite.Put(p.Key, val, leaseID)
- trace.AddField(traceutil.Field{Key: "response_revision", Value: resp.Header.Revision})
- return resp, nil
-}
-
-func DeleteRange(ctx context.Context, lg *zap.Logger, kv mvcc.KV, dr *pb.DeleteRangeRequest) (resp *pb.DeleteRangeResponse, trace *traceutil.Trace, err error) {
- trace = traceutil.Get(ctx)
- // create delete tracing if the trace in context is empty
- if trace.IsEmpty() {
- trace = traceutil.New("delete_range",
- lg,
- traceutil.Field{Key: "key", Value: string(dr.Key)},
- traceutil.Field{Key: "range_end", Value: string(dr.RangeEnd)},
- )
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
- }
- txnWrite := kv.Write(trace)
- defer txnWrite.End()
- resp, err = deleteRange(ctx, txnWrite, dr)
- return resp, trace, err
-}
-
-func deleteRange(ctx context.Context, txnWrite mvcc.TxnWrite, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
- resp := &pb.DeleteRangeResponse{}
- resp.Header = &pb.ResponseHeader{}
- end := mkGteRange(dr.RangeEnd)
-
- if dr.PrevKv {
- rr, err := txnWrite.Range(ctx, dr.Key, end, mvcc.RangeOptions{})
- if err != nil {
- return nil, err
- }
- if rr != nil {
- resp.PrevKvs = make([]*mvccpb.KeyValue, len(rr.KVs))
- for i := range rr.KVs {
- resp.PrevKvs[i] = &rr.KVs[i]
- }
- }
- }
-
- resp.Deleted, resp.Header.Revision = txnWrite.DeleteRange(dr.Key, end)
- return resp, nil
-}
-
-func Range(ctx context.Context, lg *zap.Logger, kv mvcc.KV, r *pb.RangeRequest) (resp *pb.RangeResponse, trace *traceutil.Trace, err error) {
- trace = traceutil.Get(ctx)
- if trace.IsEmpty() {
- trace = traceutil.New("range", lg)
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
- }
- defer func(start time.Time) {
- success := err == nil
- RangeSecObserve(success, time.Since(start))
- }(time.Now())
- txnRead := kv.Read(mvcc.ConcurrentReadTxMode, trace)
- defer txnRead.End()
- resp, err = executeRange(ctx, lg, txnRead, r)
- return resp, trace, err
-}
-
-func executeRange(ctx context.Context, lg *zap.Logger, txnRead mvcc.TxnRead, r *pb.RangeRequest) (*pb.RangeResponse, error) {
- trace := traceutil.Get(ctx)
-
- resp := &pb.RangeResponse{}
- resp.Header = &pb.ResponseHeader{}
-
- limit := r.Limit
- if r.SortOrder != pb.RangeRequest_NONE ||
- r.MinModRevision != 0 || r.MaxModRevision != 0 ||
- r.MinCreateRevision != 0 || r.MaxCreateRevision != 0 {
- // fetch everything; sort and truncate afterwards
- limit = 0
- }
- if limit > 0 {
- // fetch one extra for 'more' flag
- limit = limit + 1
- }
-
- ro := mvcc.RangeOptions{
- Limit: limit,
- Rev: r.Revision,
- Count: r.CountOnly,
- }
-
- rr, err := txnRead.Range(ctx, r.Key, mkGteRange(r.RangeEnd), ro)
- if err != nil {
- return nil, err
- }
-
- if r.MaxModRevision != 0 {
- f := func(kv *mvccpb.KeyValue) bool { return kv.ModRevision > r.MaxModRevision }
- pruneKVs(rr, f)
- }
- if r.MinModRevision != 0 {
- f := func(kv *mvccpb.KeyValue) bool { return kv.ModRevision < r.MinModRevision }
- pruneKVs(rr, f)
- }
- if r.MaxCreateRevision != 0 {
- f := func(kv *mvccpb.KeyValue) bool { return kv.CreateRevision > r.MaxCreateRevision }
- pruneKVs(rr, f)
- }
- if r.MinCreateRevision != 0 {
- f := func(kv *mvccpb.KeyValue) bool { return kv.CreateRevision < r.MinCreateRevision }
- pruneKVs(rr, f)
- }
-
- sortOrder := r.SortOrder
- if r.SortTarget != pb.RangeRequest_KEY && sortOrder == pb.RangeRequest_NONE {
- // Since current mvcc.Range implementation returns results
- // sorted by keys in lexiographically ascending order,
- // sort ASCEND by default only when target is not 'KEY'
- sortOrder = pb.RangeRequest_ASCEND
- } else if r.SortTarget == pb.RangeRequest_KEY && sortOrder == pb.RangeRequest_ASCEND {
- // Since current mvcc.Range implementation returns results
- // sorted by keys in lexiographically ascending order,
- // don't re-sort when target is 'KEY' and order is ASCEND
- sortOrder = pb.RangeRequest_NONE
- }
- if sortOrder != pb.RangeRequest_NONE {
- var sorter sort.Interface
- switch {
- case r.SortTarget == pb.RangeRequest_KEY:
- sorter = &kvSortByKey{&kvSort{rr.KVs}}
- case r.SortTarget == pb.RangeRequest_VERSION:
- sorter = &kvSortByVersion{&kvSort{rr.KVs}}
- case r.SortTarget == pb.RangeRequest_CREATE:
- sorter = &kvSortByCreate{&kvSort{rr.KVs}}
- case r.SortTarget == pb.RangeRequest_MOD:
- sorter = &kvSortByMod{&kvSort{rr.KVs}}
- case r.SortTarget == pb.RangeRequest_VALUE:
- sorter = &kvSortByValue{&kvSort{rr.KVs}}
- default:
- lg.Panic("unexpected sort target", zap.Int32("sort-target", int32(r.SortTarget)))
- }
- switch {
- case sortOrder == pb.RangeRequest_ASCEND:
- sort.Sort(sorter)
- case sortOrder == pb.RangeRequest_DESCEND:
- sort.Sort(sort.Reverse(sorter))
- }
- }
-
- if r.Limit > 0 && len(rr.KVs) > int(r.Limit) {
- rr.KVs = rr.KVs[:r.Limit]
- resp.More = true
- }
- trace.Step("filter and sort the key-value pairs")
- resp.Header.Revision = rr.Rev
- resp.Count = int64(rr.Count)
- resp.Kvs = make([]*mvccpb.KeyValue, len(rr.KVs))
- for i := range rr.KVs {
- if r.KeysOnly {
- rr.KVs[i].Value = nil
- }
- resp.Kvs[i] = &rr.KVs[i]
- }
- trace.Step("assemble the response")
- return resp, nil
-}
-
-func Txn(ctx context.Context, lg *zap.Logger, rt *pb.TxnRequest, txnModeWriteWithSharedBuffer bool, kv mvcc.KV, lessor lease.Lessor) (*pb.TxnResponse, *traceutil.Trace, error) {
- trace := traceutil.Get(ctx)
- if trace.IsEmpty() {
- trace = traceutil.New("transaction", lg)
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
- }
+func Txn(ctx context.Context, lg *zap.Logger, rt *pb.TxnRequest, txnModeWriteWithSharedBuffer bool, kv mvcc.KV, lessor lease.Lessor, skipRangeExecution bool) (txnResp *pb.TxnResponse, trace *traceutil.Trace, err error) {
+ ctx, trace = traceutil.EnsureTrace(ctx, lg, "transaction")
isWrite := !IsTxnReadonly(rt)
// When the transaction contains write operations, we use ReadTx instead of
// ConcurrentReadTx to avoid extra overhead of copying buffer.
@@ -275,7 +51,7 @@ func Txn(ctx context.Context, lg *zap.Logger, rt *pb.TxnRequest, txnModeWriteWit
if isWrite {
trace.AddField(traceutil.Field{Key: "read_only", Value: false})
}
- _, err := checkTxn(txnRead, rt, lessor, txnPath)
+ _, err = checkTxn(trace, txnRead, rt, lessor, txnPath)
if err != nil {
txnRead.End()
return nil, nil, err
@@ -292,7 +68,7 @@ func Txn(ctx context.Context, lg *zap.Logger, rt *pb.TxnRequest, txnModeWriteWit
} else {
txnWrite = mvcc.NewReadOnlyTxnWrite(txnRead)
}
- txnResp, err := txn(ctx, lg, txnWrite, rt, isWrite, txnPath)
+ txnResp, err = txn(ctx, lg, txnWrite, rt, isWrite, txnPath, skipRangeExecution)
txnWrite.End()
trace.AddField(
@@ -302,9 +78,9 @@ func Txn(ctx context.Context, lg *zap.Logger, rt *pb.TxnRequest, txnModeWriteWit
return txnResp, trace, err
}
-func txn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt *pb.TxnRequest, isWrite bool, txnPath []bool) (*pb.TxnResponse, error) {
+func txn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt *pb.TxnRequest, isWrite bool, txnPath []bool, skipRangeExecution bool) (*pb.TxnResponse, error) {
txnResp, _ := newTxnResp(rt, txnPath)
- _, err := executeTxn(ctx, lg, txnWrite, rt, txnPath, txnResp)
+ _, err := executeTxn(ctx, lg, txnWrite, rt, txnPath, txnResp, skipRangeExecution)
if err != nil {
if isWrite {
// CAUTION: When a txn performing write operations starts, we always expect it to be successful.
@@ -356,7 +132,7 @@ func newTxnResp(rt *pb.TxnRequest, txnPath []bool) (txnResp *pb.TxnResponse, txn
return txnResp, txnCount
}
-func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt *pb.TxnRequest, txnPath []bool, tresp *pb.TxnResponse) (txns int, err error) {
+func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt *pb.TxnRequest, txnPath []bool, tresp *pb.TxnResponse, skipRangeExecution bool) (txns int, err error) {
trace := traceutil.Get(ctx)
reqs := rt.Success
if !txnPath[0] {
@@ -371,9 +147,20 @@ func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt
traceutil.Field{Key: "req_type", Value: "range"},
traceutil.Field{Key: "range_begin", Value: string(tv.RequestRange.Key)},
traceutil.Field{Key: "range_end", Value: string(tv.RequestRange.RangeEnd)})
- resp, err := executeRange(ctx, lg, txnWrite, tv.RequestRange)
- if err != nil {
- return 0, fmt.Errorf("applyTxn: failed Range: %w", err)
+ var resp *pb.RangeResponse
+ if skipRangeExecution {
+ rev := txnWrite.Rev()
+ if len(txnWrite.Changes()) > 0 {
+ rev++
+ }
+ resp = &pb.RangeResponse{
+ Header: &pb.ResponseHeader{Revision: rev},
+ }
+ } else {
+ resp, err = executeRange(ctx, lg, txnWrite, tv.RequestRange, true)
+ if err != nil {
+ return 0, fmt.Errorf("applyTxn: failed Range: %w", err)
+ }
}
respi.(*pb.ResponseOp_ResponseRange).ResponseRange = resp
trace.StopSubTrace()
@@ -381,11 +168,12 @@ func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt
trace.StartSubTrace(
traceutil.Field{Key: "req_type", Value: "put"},
traceutil.Field{Key: "key", Value: string(tv.RequestPut.Key)},
- traceutil.Field{Key: "req_size", Value: tv.RequestPut.Size()})
- resp, err := put(ctx, txnWrite, tv.RequestPut)
+ traceutil.Field{Key: "req_size", Value: proto.Size(tv.RequestPut)})
+ prevKV, err := getPrevKV(trace, txnWrite, tv.RequestPut)
if err != nil {
- return 0, fmt.Errorf("applyTxn: failed Put: %w", err)
+ return 0, fmt.Errorf("applyTxn: failed to get prevKV on put: %w", err)
}
+ resp := put(ctx, txnWrite, tv.RequestPut, prevKV)
respi.(*pb.ResponseOp_ResponsePut).ResponsePut = resp
trace.StopSubTrace()
case *pb.RequestOp_RequestDeleteRange:
@@ -396,7 +184,7 @@ func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt
respi.(*pb.ResponseOp_ResponseDeleteRange).ResponseDeleteRange = resp
case *pb.RequestOp_RequestTxn:
resp := respi.(*pb.ResponseOp_ResponseTxn).ResponseTxn
- applyTxns, err := executeTxn(ctx, lg, txnWrite, tv.RequestTxn, txnPath[1:], resp)
+ applyTxns, err := executeTxn(ctx, lg, txnWrite, tv.RequestTxn, txnPath[1:], resp, skipRangeExecution)
if err != nil {
// don't wrap the error. It's a recursive call and err should be already wrapped
return 0, err
@@ -410,38 +198,7 @@ func executeTxn(ctx context.Context, lg *zap.Logger, txnWrite mvcc.TxnWrite, rt
return txns, nil
}
-func checkPut(rv mvcc.ReadView, lessor lease.Lessor, req *pb.PutRequest) error {
- if req.IgnoreValue || req.IgnoreLease {
- // expects previous key-value, error if not exist
- rr, err := rv.Range(context.TODO(), req.Key, nil, mvcc.RangeOptions{})
- if err != nil {
- return err
- }
- if rr == nil || len(rr.KVs) == 0 {
- return errors.ErrKeyNotFound
- }
- }
- if lease.LeaseID(req.Lease) != lease.NoLease {
- if l := lessor.Lookup(lease.LeaseID(req.Lease)); l == nil {
- return lease.ErrLeaseNotFound
- }
- }
- return nil
-}
-
-func checkRange(rv mvcc.ReadView, req *pb.RangeRequest) error {
- switch {
- case req.Revision == 0:
- return nil
- case req.Revision > rv.Rev():
- return mvcc.ErrFutureRev
- case req.Revision < rv.FirstRev():
- return mvcc.ErrCompacted
- }
- return nil
-}
-
-func checkTxn(rv mvcc.ReadView, rt *pb.TxnRequest, lessor lease.Lessor, txnPath []bool) (int, error) {
+func checkTxn(trace *traceutil.Trace, rv mvcc.ReadView, rt *pb.TxnRequest, lessor lease.Lessor, txnPath []bool) (int, error) {
txnCount := 0
reqs := rt.Success
if !txnPath[0] {
@@ -454,10 +211,10 @@ func checkTxn(rv mvcc.ReadView, rt *pb.TxnRequest, lessor lease.Lessor, txnPath
case *pb.RequestOp_RequestRange:
err = checkRange(rv, tv.RequestRange)
case *pb.RequestOp_RequestPut:
- err = checkPut(rv, lessor, tv.RequestPut)
+ err = checkPut(trace, rv, lessor, tv.RequestPut)
case *pb.RequestOp_RequestDeleteRange:
case *pb.RequestOp_RequestTxn:
- txns, err = checkTxn(rv, tv.RequestTxn, lessor, txnPath[1:])
+ txns, err = checkTxn(trace, rv, tv.RequestTxn, lessor, txnPath[1:])
txnCount += txns + 1
txnPath = txnPath[txns+1:]
default:
@@ -470,67 +227,6 @@ func checkTxn(rv mvcc.ReadView, rt *pb.TxnRequest, lessor lease.Lessor, txnPath
return txnCount, nil
}
-// mkGteRange determines if the range end is a >= range. This works around grpc
-// sending empty byte strings as nil; >= is encoded in the range end as '\0'.
-// If it is a GTE range, then []byte{} is returned to indicate the empty byte
-// string (vs nil being no byte string).
-func mkGteRange(rangeEnd []byte) []byte {
- if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
- return []byte{}
- }
- return rangeEnd
-}
-
-func pruneKVs(rr *mvcc.RangeResult, isPrunable func(*mvccpb.KeyValue) bool) {
- j := 0
- for i := range rr.KVs {
- rr.KVs[j] = rr.KVs[i]
- if !isPrunable(&rr.KVs[i]) {
- j++
- }
- }
- rr.KVs = rr.KVs[:j]
-}
-
-type kvSort struct{ kvs []mvccpb.KeyValue }
-
-func (s *kvSort) Swap(i, j int) {
- t := s.kvs[i]
- s.kvs[i] = s.kvs[j]
- s.kvs[j] = t
-}
-func (s *kvSort) Len() int { return len(s.kvs) }
-
-type kvSortByKey struct{ *kvSort }
-
-func (s *kvSortByKey) Less(i, j int) bool {
- return bytes.Compare(s.kvs[i].Key, s.kvs[j].Key) < 0
-}
-
-type kvSortByVersion struct{ *kvSort }
-
-func (s *kvSortByVersion) Less(i, j int) bool {
- return (s.kvs[i].Version - s.kvs[j].Version) < 0
-}
-
-type kvSortByCreate struct{ *kvSort }
-
-func (s *kvSortByCreate) Less(i, j int) bool {
- return (s.kvs[i].CreateRevision - s.kvs[j].CreateRevision) < 0
-}
-
-type kvSortByMod struct{ *kvSort }
-
-func (s *kvSortByMod) Less(i, j int) bool {
- return (s.kvs[i].ModRevision - s.kvs[j].ModRevision) < 0
-}
-
-type kvSortByValue struct{ *kvSort }
-
-func (s *kvSortByValue) Less(i, j int) bool {
- return bytes.Compare(s.kvs[i].Value, s.kvs[j].Value) < 0
-}
-
func compareInt64(a, b int64) int {
switch {
case a < b:
@@ -585,7 +281,7 @@ func applyCompare(rv mvcc.ReadView, c *pb.Compare) bool {
// nil == empty string in grpc; no way to represent missing value
return false
}
- return compareKV(c, mvccpb.KeyValue{})
+ return compareKV(c, &mvccpb.KeyValue{})
}
for _, kv := range rr.KVs {
if !compareKV(c, kv) {
@@ -595,7 +291,7 @@ func applyCompare(rv mvcc.ReadView, c *pb.Compare) bool {
return true
}
-func compareKV(c *pb.Compare, ckv mvccpb.KeyValue) bool {
+func compareKV(c *pb.Compare, ckv *mvccpb.KeyValue) bool {
var result int
rev := int64(0)
switch c.Target {
@@ -666,71 +362,3 @@ func IsTxnReadonly(r *pb.TxnRequest) bool {
}
return true
}
-
-func CheckTxnAuth(as auth.AuthStore, ai *auth.AuthInfo, rt *pb.TxnRequest) error {
- return checkTxnPermission(as, ai, rt)
-}
-
-func checkTxnPermission(as auth.AuthStore, ai *auth.AuthInfo, rt *pb.TxnRequest) error {
- for _, c := range rt.Compare {
- if err := as.IsRangePermitted(ai, c.Key, c.RangeEnd); err != nil {
- return err
- }
- }
- if err := checkTxnReqsPermission(as, ai, rt.Success); err != nil {
- return err
- }
- return checkTxnReqsPermission(as, ai, rt.Failure)
-}
-
-func checkTxnReqsPermission(as auth.AuthStore, ai *auth.AuthInfo, reqs []*pb.RequestOp) error {
- for _, requ := range reqs {
- switch tv := requ.Request.(type) {
- case *pb.RequestOp_RequestRange:
- if tv.RequestRange == nil {
- continue
- }
-
- if err := as.IsRangePermitted(ai, tv.RequestRange.Key, tv.RequestRange.RangeEnd); err != nil {
- return err
- }
-
- case *pb.RequestOp_RequestPut:
- if tv.RequestPut == nil {
- continue
- }
-
- if err := as.IsPutPermitted(ai, tv.RequestPut.Key); err != nil {
- return err
- }
-
- case *pb.RequestOp_RequestDeleteRange:
- if tv.RequestDeleteRange == nil {
- continue
- }
-
- if tv.RequestDeleteRange.PrevKv {
- err := as.IsRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
- if err != nil {
- return err
- }
- }
-
- err := as.IsDeleteRangePermitted(ai, tv.RequestDeleteRange.Key, tv.RequestDeleteRange.RangeEnd)
- if err != nil {
- return err
- }
- case *pb.RequestOp_RequestTxn:
- if tv.RequestTxn == nil {
- continue
- }
-
- err := checkTxnPermission(as, ai, tv.RequestTxn)
- if err != nil {
- return err
- }
- }
- }
-
- return nil
-}
diff --git a/server/etcdserver/txn/txn_test.go b/server/etcdserver/txn/txn_test.go
index a094830dd8fc..7740e9e41e10 100644
--- a/server/etcdserver/txn/txn_test.go
+++ b/server/etcdserver/txn/txn_test.go
@@ -21,21 +21,16 @@ import (
"os"
"strings"
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
- "go.etcd.io/etcd/api/v3/authpb"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/pkg/v3/traceutil"
- "go.etcd.io/etcd/server/v3/auth"
"go.etcd.io/etcd/server/v3/lease"
- "go.etcd.io/etcd/server/v3/storage/backend"
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
"go.etcd.io/etcd/server/v3/storage/mvcc"
- "go.etcd.io/etcd/server/v3/storage/schema"
)
type testCase struct {
@@ -226,9 +221,9 @@ func TestCheckTxn(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
s, lessor := setup(t, tc.setup)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
- _, _, err := Txn(ctx, zaptest.NewLogger(t), tc.txn, false, s, lessor)
+ _, _, err := Txn(ctx, zaptest.NewLogger(t), tc.txn, false, s, lessor, false)
gotErr := ""
if err != nil {
@@ -246,7 +241,7 @@ func TestCheckPut(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
s, lessor := setup(t, tc.setup)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
_, _, err := Put(ctx, zaptest.NewLogger(t), lessor, s, tc.op.GetRequestPut())
@@ -266,9 +261,9 @@ func TestCheckRange(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
s, _ := setup(t, tc.setup)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
- _, _, err := Range(ctx, zaptest.NewLogger(t), s, tc.op.GetRequestRange())
+ _, _, err := Range(ctx, zaptest.NewLogger(t), s, tc.op.GetRequestRange(), true)
gotErr := ""
if err != nil {
@@ -307,6 +302,76 @@ func setup(t *testing.T, setup testSetup) (mvcc.KV, lease.Lessor) {
return s, lessor
}
+func TestSkippedRangeExecutionResponseHeaderRevision(t *testing.T) {
+ tests := []struct {
+ name string
+ txn *pb.TxnRequest
+ }{
+ {
+ name: "range before write",
+ txn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: &pb.RangeRequest{Key: []byte("foo")},
+ },
+ },
+ {
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")},
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "range after write",
+ txn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")},
+ },
+ },
+ {
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: &pb.RangeRequest{Key: []byte("foo")},
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := t.Context()
+ lg := zaptest.NewLogger(t)
+ normalKV, normalLessor := setup(t, testSetup{})
+ skippedKV, skippedLessor := setup(t, testSetup{})
+
+ normalResp, _, err := Txn(ctx, lg, tt.txn, false, normalKV, normalLessor, false)
+ require.NoError(t, err)
+ skippedResp, _, err := Txn(ctx, lg, tt.txn, false, skippedKV, skippedLessor, true)
+ require.NoError(t, err)
+
+ var normalRangeHeader, skippedRangeHeader *pb.ResponseHeader
+ for i, resp := range normalResp.Responses {
+ rangeResp := resp.GetResponseRange()
+ if rangeResp == nil {
+ continue
+ }
+ normalRangeHeader = rangeResp.Header
+ skippedRangeHeader = skippedResp.Responses[i].GetResponseRange().Header
+ break
+ }
+ require.NotNil(t, normalRangeHeader)
+ require.NotNil(t, skippedRangeHeader)
+ require.Equal(t, normalRangeHeader.Revision, skippedRangeHeader.Revision)
+ })
+ }
+}
+
func TestReadonlyTxnError(t *testing.T) {
b, _ := betesting.NewDefaultTmpBackend(t)
defer betesting.Close(t, b)
@@ -314,7 +379,7 @@ func TestReadonlyTxnError(t *testing.T) {
defer s.Close()
// setup cancelled context
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
// put some data to prevent early termination in rangeKeys
@@ -333,7 +398,7 @@ func TestReadonlyTxnError(t *testing.T) {
},
}
- _, _, err := Txn(ctx, zaptest.NewLogger(t), txn, false, s, &lease.FakeLessor{})
+ _, _, err := Txn(ctx, zaptest.NewLogger(t), txn, false, s, &lease.FakeLessor{}, false)
if err == nil || !strings.Contains(err.Error(), "applyTxn: failed Range: rangeKeys: context cancelled: context canceled") {
t.Fatalf("Expected context canceled error, got %v", err)
}
@@ -345,7 +410,7 @@ func TestWriteTxnPanicWithoutApply(t *testing.T) {
defer s.Close()
// setup cancelled context
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
// write txn that puts some data and then fails in range due to cancelled context
@@ -376,292 +441,12 @@ func TestWriteTxnPanicWithoutApply(t *testing.T) {
// we verify the following properties below:
// 1. server panics after a write txn aply fails (invariant: server should never try to move on from a failed write)
// 2. no writes from the txn are applied to the backend (invariant: failed write should have no side-effect on DB state besides panic)
- assert.Panicsf(t, func() { Txn(ctx, zaptest.NewLogger(t), txn, false, s, &lease.FakeLessor{}) }, "Expected panic in Txn with writes")
+ assert.Panicsf(t, func() { Txn(ctx, zaptest.NewLogger(t), txn, false, s, &lease.FakeLessor{}, false) }, "Expected panic in Txn with writes")
dbHashAfter, err := computeFileHash(bePath)
require.NoErrorf(t, err, "failed to compute DB file hash after txn")
require.Equalf(t, dbHashBefore, dbHashAfter, "mismatch in DB hash before and after failed write txn")
}
-func TestCheckTxnAuth(t *testing.T) {
- be, _ := betesting.NewDefaultTmpBackend(t)
- defer betesting.Close(t, be)
- as := setupAuth(t, be)
-
- tests := []struct {
- name string
- txnRequest *pb.TxnRequest
- err error
- }{
- {
- name: "Out of range compare is unauthorized",
- txnRequest: &pb.TxnRequest{
- Compare: []*pb.Compare{outOfRangeCompare},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "In range compare is authorized",
- txnRequest: &pb.TxnRequest{
- Compare: []*pb.Compare{inRangeCompare},
- },
- err: nil,
- },
- {
- name: "Nil request range is always authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{nilRequestRange},
- },
- err: nil,
- },
- {
- name: "Range request in range is authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestRange},
- Failure: []*pb.RequestOp{inRangeRequestRange},
- },
- err: nil,
- },
- {
- name: "Range request out of range success case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestRange},
- Failure: []*pb.RequestOp{inRangeRequestRange},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Range request out of range failure case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestRange},
- Failure: []*pb.RequestOp{outOfRangeRequestRange},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Nil Put request is always authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{nilRequestPut},
- },
- err: nil,
- },
- {
- name: "Put request in range in authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestPut},
- Failure: []*pb.RequestOp{inRangeRequestPut},
- },
- err: nil,
- },
- {
- name: "Put request out of range success case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestPut},
- Failure: []*pb.RequestOp{inRangeRequestPut},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Put request out of range failure case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestPut},
- Failure: []*pb.RequestOp{outOfRangeRequestPut},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Nil delete request is authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{nilRequestDeleteRange},
- },
- err: nil,
- },
- {
- name: "Delete range request in range is authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestDeleteRange},
- Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
- },
- err: nil,
- },
- {
- name: "Delete range request out of range success case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
- Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Delete range request out of range failure case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestDeleteRange},
- Failure: []*pb.RequestOp{outOfRangeRequestDeleteRange},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Delete range request out of range and PrevKv false success case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestDeleteRangeKvFalse},
- Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Delete range request out of range and PrevKv false failure case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestDeleteRange},
- Failure: []*pb.RequestOp{outOfRangeRequestDeleteRangeKvFalse},
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Nested txn request in range is authorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Success: []*pb.RequestOp{inRangeRequestRange, inRangeRequestPut},
- Failure: []*pb.RequestOp{inRangeRequestDeleteRange},
- },
- },
- },
- },
- },
- err: nil,
- },
- {
- name: "Nested txn request out of range success case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestRange},
- },
- },
- },
- },
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Nested txn request out of range failure case is unauthorized",
- txnRequest: &pb.TxnRequest{
- Failure: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Failure: []*pb.RequestOp{outOfRangeRequestPut},
- },
- },
- },
- },
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Nested txn request out of range delete is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
- },
- },
- },
- },
- },
- err: auth.ErrPermissionDenied,
- },
- {
- name: "Two level nested txn request out of range delete is unauthorized",
- txnRequest: &pb.TxnRequest{
- Success: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Failure: []*pb.RequestOp{
- {
- Request: &pb.RequestOp_RequestTxn{
- RequestTxn: &pb.TxnRequest{
- Success: []*pb.RequestOp{outOfRangeRequestDeleteRange},
- },
- },
- },
- },
- },
- },
- },
- },
- },
- err: auth.ErrPermissionDenied,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := CheckTxnAuth(as, &auth.AuthInfo{Username: "foo", Revision: 8}, tt.txnRequest)
- assert.Equal(t, tt.err, err)
- })
- }
-}
-
-// CheckTxnAuth test setup.
-func setupAuth(t *testing.T, be backend.Backend) auth.AuthStore {
- lg := zaptest.NewLogger(t)
-
- simpleTokenTTLDefault := 300 * time.Second
- tokenTypeSimple := "simple"
- dummyIndexWaiter := func(index uint64) <-chan struct{} {
- ch := make(chan struct{}, 1)
- go func() {
- ch <- struct{}{}
- }()
- return ch
- }
-
- tp, _ := auth.NewTokenProvider(zaptest.NewLogger(t), tokenTypeSimple, dummyIndexWaiter, simpleTokenTTLDefault)
-
- as := auth.NewAuthStore(lg, schema.NewAuthBackend(lg, be), tp, 4)
-
- // create "root" user and "foo" user with limited range
- _, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "root"})
- require.NoError(t, err)
-
- _, err = as.RoleAdd(&pb.AuthRoleAddRequest{Name: "rw"})
- require.NoError(t, err)
-
- _, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
- Name: "rw",
- Perm: &authpb.Permission{
- PermType: authpb.READWRITE,
- Key: []byte("foo"),
- RangeEnd: []byte("zoo"),
- },
- })
- require.NoError(t, err)
-
- _, err = as.UserAdd(&pb.AuthUserAddRequest{Name: "root", Password: "foo"})
- require.NoError(t, err)
-
- _, err = as.UserAdd(&pb.AuthUserAddRequest{Name: "foo", Password: "foo"})
- require.NoError(t, err)
-
- _, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "root", Role: "root"})
- require.NoError(t, err)
-
- _, err = as.UserGrantRole(&pb.AuthUserGrantRoleRequest{User: "foo", Role: "rw"})
- require.NoError(t, err)
-
- err = as.AuthEnable()
- require.NoError(t, err)
-
- return as
-}
-
func computeFileHash(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
@@ -675,87 +460,3 @@ func computeFileHash(filePath string) (string, error) {
}
return string(h.Sum(nil)), nil
}
-
-// CheckTxnAuth variables setup.
-var (
- inRangeCompare = &pb.Compare{
- Key: []byte("foo"),
- RangeEnd: []byte("zoo"),
- }
- outOfRangeCompare = &pb.Compare{
- Key: []byte("boo"),
- RangeEnd: []byte("zoo"),
- }
- nilRequestPut = &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{
- RequestPut: nil,
- },
- }
- inRangeRequestPut = &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{
- RequestPut: &pb.PutRequest{
- Key: []byte("foo"),
- },
- },
- }
- outOfRangeRequestPut = &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{
- RequestPut: &pb.PutRequest{
- Key: []byte("boo"),
- },
- },
- }
- nilRequestRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestRange{
- RequestRange: nil,
- },
- }
- inRangeRequestRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestRange{
- RequestRange: &pb.RangeRequest{
- Key: []byte("foo"),
- RangeEnd: []byte("zoo"),
- },
- },
- }
- outOfRangeRequestRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestRange{
- RequestRange: &pb.RangeRequest{
- Key: []byte("boo"),
- RangeEnd: []byte("zoo"),
- },
- },
- }
- nilRequestDeleteRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestDeleteRange{
- RequestDeleteRange: nil,
- },
- }
- inRangeRequestDeleteRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestDeleteRange{
- RequestDeleteRange: &pb.DeleteRangeRequest{
- Key: []byte("foo"),
- RangeEnd: []byte("zoo"),
- PrevKv: true,
- },
- },
- }
- outOfRangeRequestDeleteRange = &pb.RequestOp{
- Request: &pb.RequestOp_RequestDeleteRange{
- RequestDeleteRange: &pb.DeleteRangeRequest{
- Key: []byte("boo"),
- RangeEnd: []byte("zoo"),
- PrevKv: true,
- },
- },
- }
- outOfRangeRequestDeleteRangeKvFalse = &pb.RequestOp{
- Request: &pb.RequestOp_RequestDeleteRange{
- RequestDeleteRange: &pb.DeleteRangeRequest{
- Key: []byte("boo"),
- RangeEnd: []byte("zoo"),
- PrevKv: false,
- },
- },
- }
-)
diff --git a/server/etcdserver/txn/util.go b/server/etcdserver/txn/util.go
index f9987c6d5d1c..1a09415e9cba 100644
--- a/server/etcdserver/txn/util.go
+++ b/server/etcdserver/txn/util.go
@@ -20,8 +20,8 @@ import (
"strings"
"time"
- "github.com/golang/protobuf/proto"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
@@ -72,7 +72,7 @@ func WarnOfExpensiveReadOnlyTxnRequest(lg *zap.Logger, warningApplyDuration time
// only range responses should be in a read only txn request
}
}
- resp = fmt.Sprintf("responses:<%s> size:%d", strings.Join(resps, " "), txnResponse.Size())
+ resp = fmt.Sprintf("responses:<%s> size:%d", strings.Join(resps, " "), proto.Size(txnResponse))
}
warnOfExpensiveGenericRequest(lg, warningApplyDuration, now, reqStringer, "read-only txn ", resp, err)
}
@@ -83,7 +83,7 @@ func WarnOfExpensiveReadOnlyRangeRequest(lg *zap.Logger, warningApplyDuration ti
}
var resp string
if !isNil(rangeResponse) {
- resp = fmt.Sprintf("range_response_count:%d size:%d", len(rangeResponse.Kvs), rangeResponse.Size())
+ resp = fmt.Sprintf("range_response_count:%d size:%d", len(rangeResponse.Kvs), proto.Size(rangeResponse))
}
warnOfExpensiveGenericRequest(lg, warningApplyDuration, now, reqStringer, "read-only range ", resp, err)
}
diff --git a/server/etcdserver/txn/util_bench_test.go b/server/etcdserver/txn/util_bench_test.go
index 5a84f62f537d..7f9f1292a624 100644
--- a/server/etcdserver/txn/util_bench_test.go
+++ b/server/etcdserver/txn/util_bench_test.go
@@ -16,39 +16,47 @@ package txn
import (
"errors"
+ "math/rand/v2"
"testing"
"time"
- "go.uber.org/zap/zaptest"
+ "go.uber.org/zap"
- "go.etcd.io/raft/v3/raftpb"
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
)
-func BenchmarkWarnOfExpensiveRequestNoLog(b *testing.B) {
- m := &raftpb.Message{
- Type: 0,
- To: 0,
- From: 1,
- Term: 2,
- LogTerm: 3,
- Index: 0,
- Entries: []raftpb.Entry{
- {
- Term: 0,
- Index: 0,
- Type: 0,
- Data: make([]byte, 1024),
+func benchmarkRangeResponse() *pb.RangeResponse {
+ value := make([]byte, 1024)
+ rng := rand.New(rand.NewPCG(1, 2))
+ for i := range value {
+ value[i] = byte(rng.Uint64())
+ }
+ return &pb.RangeResponse{
+ Count: 1000,
+ Kvs: []*mvccpb.KeyValue{
+ {Key: []byte("/pods/1"), Value: value},
+ },
+ }
+}
+
+func BenchmarkWarnOfExpensiveRequestWithProtoSize(b *testing.B) {
+ reqStringer := &pb.InternalRaftStringer{
+ Request: &pb.InternalRaftRequest{
+ Header: &pb.RequestHeader{ID: 1},
+ Range: &pb.RangeRequest{
+ Key: []byte("/pods"),
+ RangeEnd: []byte("/pods\x00"),
},
},
- Commit: 0,
- Snapshot: nil,
- Reject: false,
- RejectHint: 0,
- Context: nil,
}
+ respMsg := benchmarkRangeResponse()
err := errors.New("benchmarking warn of expensive request")
- lg := zaptest.NewLogger(b)
+ lg := zap.NewNop()
+ now := time.Now().Add(-time.Second)
+ b.ReportAllocs()
+ b.ResetTimer()
for n := 0; n < b.N; n++ {
- WarnOfExpensiveRequest(lg, time.Second, time.Now(), nil, m, err)
+ WarnOfExpensiveRequest(lg, 0, now, reqStringer, respMsg, err)
}
}
diff --git a/server/etcdserver/util.go b/server/etcdserver/util.go
index fbba5491b071..639413562298 100644
--- a/server/etcdserver/util.go
+++ b/server/etcdserver/util.go
@@ -15,18 +15,36 @@
package etcdserver
import (
- "fmt"
"time"
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
)
-// isConnectedToQuorumSince checks whether the local member is connected to the
-// quorum of the cluster since the given time.
+// isConnectedToQuorumSince reports whether the local member has been connected
+// to a quorum of the current cluster continuously since the given time.
func isConnectedToQuorumSince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) bool {
- return numConnectedSince(transport, since, self, members) >= (len(members)/2)+1
+ return numConnectedSince(transport, since, self, members) >= quorum(len(members))
+}
+
+// isConnectedToQuorumAfterAddingNewMemberSince reports whether the local member
+// has been connected to a quorum continuously since the given time, assuming a
+// new member is being added to the cluster.
+//
+// For a single-member cluster, it always returns true to allow membership
+// expansion.
+func isConnectedToQuorumAfterAddingNewMemberSince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) bool {
+ if len(members) == 1 {
+ // If it's a single member cluster, we should allow adding a new member
+ return true
+ }
+ return numConnectedSince(transport, since, self, members) >= quorum(len(members)+1)
+}
+
+func quorum(num int) int {
+ return num/2 + 1
}
// isConnectedSince checks whether the local member is connected to the
@@ -36,10 +54,23 @@ func isConnectedSince(transport rafthttp.Transporter, since time.Time, remote ty
return !t.IsZero() && t.Before(since)
}
-// isConnectedFullySince checks whether the local member is connected to all
-// members in the cluster since the given time.
-func isConnectedFullySince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*membership.Member) bool {
- return numConnectedSince(transport, since, self, members) == len(members)
+// exceedsRequestLimit checks if the committed index is too far ahead of the applied index.
+// LeaseRevoke requests are prioritized to ensure timely lease expiration,
+// which helps mitigate pressure on the cluster.
+func exceedsRequestLimit(appliedIndex, committedIndex uint64, r *pb.InternalRaftRequest, enablePriority bool) bool {
+ if committedIndex <= appliedIndex+maxNormalGap {
+ return false
+ }
+ if enablePriority && isPriorityRequest(r) {
+ if committedIndex <= appliedIndex+maxPriorityGap {
+ return false
+ }
+ }
+ return true
+}
+
+func isPriorityRequest(r *pb.InternalRaftRequest) bool {
+ return r != nil && r.LeaseRevoke != nil
}
// numConnectedSince counts how many members are connected to the local member
@@ -80,37 +111,3 @@ func longestConnected(tp rafthttp.Transporter, membs []types.ID) (types.ID, bool
}
return longest, true
}
-
-type notifier struct {
- c chan struct{}
- err error
-}
-
-func newNotifier() *notifier {
- return ¬ifier{
- c: make(chan struct{}),
- }
-}
-
-func (nc *notifier) notify(err error) {
- nc.err = err
- close(nc.c)
-}
-
-// panicAlternativeStringer wraps a fmt.Stringer, and if calling String() panics, calls the alternative instead.
-// This is needed to ensure logging slow v2 requests does not panic, which occurs when running integration tests
-// with the embedded server with github.com/golang/protobuf v1.4.0+. See https://github.com/etcd-io/etcd/issues/12197.
-type panicAlternativeStringer struct {
- stringer fmt.Stringer
- alternative func() string
-}
-
-func (n panicAlternativeStringer) String() (s string) {
- defer func() {
- if err := recover(); err != nil {
- s = n.alternative()
- }
- }()
- s = n.stringer.String()
- return s
-}
diff --git a/server/etcdserver/util_test.go b/server/etcdserver/util_test.go
index cad7c3cf4528..d16fdb8a8c39 100644
--- a/server/etcdserver/util_test.go
+++ b/server/etcdserver/util_test.go
@@ -77,8 +77,8 @@ func newNopTransporterWithActiveTime(memberIDs []types.ID) rafthttp.Transporter
func (s *nopTransporterWithActiveTime) Start() error { return nil }
func (s *nopTransporterWithActiveTime) Handler() http.Handler { return nil }
-func (s *nopTransporterWithActiveTime) Send(m []raftpb.Message) {}
-func (s *nopTransporterWithActiveTime) SendSnapshot(m snap.Message) {}
+func (s *nopTransporterWithActiveTime) Send(m []*raftpb.Message) {}
+func (s *nopTransporterWithActiveTime) SendSnapshot(m *snap.Message) {}
func (s *nopTransporterWithActiveTime) AddRemote(id types.ID, us []string) {}
func (s *nopTransporterWithActiveTime) AddPeer(id types.ID, us []string) {}
func (s *nopTransporterWithActiveTime) RemovePeer(id types.ID) {}
@@ -90,23 +90,3 @@ func (s *nopTransporterWithActiveTime) Stop() {}
func (s *nopTransporterWithActiveTime) Pause() {}
func (s *nopTransporterWithActiveTime) Resume() {}
func (s *nopTransporterWithActiveTime) reset(am map[types.ID]time.Time) { s.activeMap = am }
-
-func TestPanicAlternativeStringer(t *testing.T) {
- p := panicAlternativeStringer{alternative: func() string { return "alternative" }}
-
- p.stringer = testStringerFunc(func() string { panic("here") })
- if s := p.String(); s != "alternative" {
- t.Fatalf("expected 'alternative', got %q", s)
- }
-
- p.stringer = testStringerFunc(func() string { return "test" })
- if s := p.String(); s != "test" {
- t.Fatalf("expected 'test', got %q", s)
- }
-}
-
-type testStringerFunc func() string
-
-func (s testStringerFunc) String() string {
- return s()
-}
diff --git a/server/etcdserver/v3_server.go b/server/etcdserver/v3_server.go
index 2e667b6d156e..a357705a07e5 100644
--- a/server/etcdserver/v3_server.go
+++ b/server/etcdserver/v3_server.go
@@ -17,14 +17,16 @@ package etcdserver
import (
"context"
"encoding/base64"
- "encoding/binary"
errorspkg "errors"
+ "math"
"strconv"
"time"
- "github.com/gogo/protobuf/proto"
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
+ "google.golang.org/protobuf/proto"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/version"
@@ -38,7 +40,6 @@ import (
"go.etcd.io/etcd/server/v3/lease"
"go.etcd.io/etcd/server/v3/lease/leasehttp"
"go.etcd.io/etcd/server/v3/storage/mvcc"
- "go.etcd.io/raft/v3"
)
const (
@@ -47,8 +48,9 @@ const (
// However, if the committed entries are very heavy to toApply, the gap might grow.
// We should stop accepting new proposals if the gap growing to a certain point.
maxGapBetweenApplyAndCommitIndex = 5000
+ maxNormalGap = maxGapBetweenApplyAndCommitIndex
+ maxPriorityGap = 2 * maxGapBetweenApplyAndCommitIndex
traceThreshold = 100 * time.Millisecond
- readIndexRetryTime = 500 * time.Millisecond
// The timeout for the node to catch up its applied index, and is used in
// lease related operations, such as LeaseRenew and LeaseTimeToLive.
@@ -57,6 +59,7 @@ const (
type RaftKV interface {
Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error)
+ RangeStream(r *pb.RangeRequest, rs pb.KV_RangeStreamServer) error
Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error)
DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error)
Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error)
@@ -101,12 +104,21 @@ type Authenticator interface {
}
func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
- trace := traceutil.New("range",
- s.Logger(),
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "range", trace.WithAttributes(
+ attribute.String("range_begin", string(r.GetKey())),
+ attribute.String("range_end", string(r.GetRangeEnd())),
+ attribute.Int64("rev", r.GetRevision()),
+ attribute.Int64("limit", r.GetLimit()),
+ attribute.Bool("count_only", r.GetCountOnly()),
+ attribute.Bool("keys_only", r.GetKeysOnly()),
+ ))
+ defer span.End()
+
+ ctx, trace := traceutil.EnsureTrace(ctx, s.Logger(), "range",
traceutil.Field{Key: "range_begin", Value: string(r.Key)},
traceutil.Field{Key: "range_end", Value: string(r.RangeEnd)},
)
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
var resp *pb.RangeResponse
var err error
@@ -119,10 +131,12 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe
)
}
trace.LogIfLong(traceThreshold)
+ success := err == nil
+ requestDurationSec.WithLabelValues("Range", strconv.FormatBool(success)).Observe(time.Since(start).Seconds())
}(time.Now())
if !r.Serializable {
- err = s.linearizableReadNotify(ctx)
+ err = s.read.LinearizableReadNotify(ctx)
trace.Step("agreement among raft nodes before linearized reading")
if err != nil {
return nil, err
@@ -132,7 +146,7 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe
return s.authStore.IsRangePermitted(ai, r.Key, r.RangeEnd)
}
- get := func() { resp, _, err = txn.Range(ctx, s.Logger(), s.KV(), r) }
+ get := func() { resp, _, err = txn.Range(ctx, s.Logger(), s.KV(), r, true) }
if serr := s.doSerialize(ctx, chk, get); serr != nil {
err = serr
return nil, err
@@ -140,9 +154,153 @@ func (s *EtcdServer) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRe
return resp, err
}
+func (s *EtcdServer) RangeStream(r *pb.RangeRequest, rs pb.KV_RangeStreamServer) error {
+ ctx := rs.Context()
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "range_streaming", trace.WithAttributes(
+ attribute.String("range_begin", string(r.GetKey())),
+ attribute.String("range_end", string(r.GetRangeEnd())),
+ attribute.Int64("rev", r.GetRevision()),
+ attribute.Int64("limit", r.GetLimit()),
+ attribute.Bool("count_only", r.GetCountOnly()),
+ ))
+ defer span.End()
+
+ ctx, trace := traceutil.EnsureTrace(ctx, s.Logger(), "range_streaming",
+ traceutil.Field{Key: "range_begin", Value: string(r.Key)},
+ traceutil.Field{Key: "range_end", Value: string(r.RangeEnd)},
+ )
+
+ if !r.Serializable {
+ err := s.read.LinearizableReadNotify(ctx)
+ trace.Step("agreement among raft nodes before linearized reading")
+ if err != nil {
+ return err
+ }
+ }
+
+ chk := func(ai *auth.AuthInfo) error {
+ return s.authStore.IsRangePermitted(ai, r.Key, r.RangeEnd)
+ }
+
+ var err error
+ get := func() {
+ err = s.rangeStream(ctx, r, rs)
+ }
+ if serr := s.doSerialize(ctx, chk, get); serr != nil {
+ err = serr
+ return err
+ }
+ return err
+}
+
+func (s *EtcdServer) rangeStream(ctx context.Context, r *pb.RangeRequest, rs pb.KV_RangeStreamServer) error {
+ if r.CountOnly {
+ resp, _, err := txn.Range(ctx, s.Logger(), s.KV(), r, false)
+ if err != nil {
+ return err
+ }
+ out := &pb.RangeResponse{
+ Header: &pb.ResponseHeader{Revision: resp.Header.Revision},
+ Count: resp.Count,
+ }
+ return rs.Send(&pb.RangeStreamResponse{RangeResponse: out})
+ }
+
+ totalLimit := r.Limit
+ if totalLimit == 0 {
+ totalLimit = math.MaxInt64
+ }
+ r.Limit = initialStreamChunkLimit
+ if r.Limit > totalLimit {
+ r.Limit = totalLimit
+ }
+
+ count := int64(0)
+ var headerRev int64
+ for {
+ // gofail: var beforeRangeStreamChunk struct{}
+ resp, _, err := txn.Range(ctx, s.Logger(), s.KV(), r, false)
+ if err != nil {
+ return err
+ }
+ // headerRev should represent the latest store revision at the moment
+ // the server starts handling the client request, and remain stable for
+ // the whole response stream.
+ //
+ // If the client did not explicitly pin a revision (r.Revision == 0),
+ // we pin it here to that same initial latest revision, rather than
+ // advancing it for each subsequent range response in the stream.
+ // As a result, writes committed after the stream begins are not reflected
+ // in later range responses of this stream.
+ if headerRev == 0 {
+ headerRev = resp.Header.Revision
+ if r.Revision == 0 {
+ r.Revision = headerRev
+ }
+ }
+ count += int64(len(resp.Kvs))
+
+ var nextKey []byte
+ if resp.More {
+ nextKey = append(resp.Kvs[len(resp.Kvs)-1].Key, '\x00')
+ }
+ out := &pb.RangeResponse{Kvs: resp.Kvs}
+ done := !resp.More || count == totalLimit
+ if done {
+ out.Header = &pb.ResponseHeader{Revision: headerRev}
+ out.More = resp.More
+ out.Count = count
+ if resp.More {
+ remaining, cerr := txn.Count(ctx, s.Logger(), s.KV(), nextKey, r.RangeEnd, r.Revision)
+ if cerr != nil {
+ return cerr
+ }
+ out.Count += remaining
+ }
+ }
+ if err := rs.Send(&pb.RangeStreamResponse{RangeResponse: out}); err != nil {
+ return err
+ }
+ if done {
+ return nil
+ }
+
+ r.Key = nextKey
+ r.Limit = adjustChunkLimit(r.Limit, proto.Size(resp), int(s.Cfg.MaxRequestBytes))
+ r.Limit = min(r.Limit, totalLimit-count)
+ }
+}
+
+const initialStreamChunkLimit = 10
+
+// adjustChunkLimit picks the next chunk's Limit so each chunk lands near
+// the target size: too small wastes round-trips, too large produces
+// oversized chunks. Doubling/halving only outside [0.5x, 2x] avoids
+// thrashing when responses sit near the boundary. Always returns >= 1,
+// since Limit=0 means "unlimited" in txn.Range.
+func adjustChunkLimit(lastLimit int64, lastSize, targetSize int) int64 {
+ switch {
+ case lastSize < targetSize/2:
+ lastLimit *= 2
+ case lastSize > targetSize*2:
+ lastLimit /= 2
+ }
+ if lastLimit == 0 {
+ lastLimit = 1
+ }
+ return lastLimit
+}
+
func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "put", trace.WithAttributes(
+ attribute.String("key", string(r.GetKey())),
+ ))
+ defer span.End()
+
ctx = context.WithValue(ctx, traceutil.StartTimeKey{}, time.Now())
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{Put: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{Put: r})
if err != nil {
return nil, err
}
@@ -150,7 +308,14 @@ func (s *EtcdServer) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse
}
func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{DeleteRange: r})
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "delete_range", trace.WithAttributes(
+ attribute.String("range_begin", string(r.GetKey())),
+ attribute.String("range_end", string(r.GetRangeEnd())),
+ ))
+ defer span.End()
+
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{DeleteRange: r})
if err != nil {
return nil, err
}
@@ -158,14 +323,27 @@ func (s *EtcdServer) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest)
}
func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
- if txn.IsTxnReadonly(r) {
- trace := traceutil.New("transaction",
- s.Logger(),
- traceutil.Field{Key: "read_only", Value: true},
- )
- ctx = context.WithValue(ctx, traceutil.TraceKey{}, trace)
+ readOnly := txn.IsTxnReadonly(r)
+
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "txn", trace.WithAttributes(
+ attribute.String("compare_first_key", firstCompareKey(r.GetCompare())),
+ attribute.String("success_first_key", firstOpKey(r.GetSuccess())),
+ attribute.String("success_first_type", firstOpType(r.GetSuccess())),
+ attribute.Int64("success_first_lease", firstOpLease(r.GetSuccess())),
+ attribute.Int("compare_len", len(r.GetCompare())),
+ attribute.Int("success_len", len(r.GetSuccess())),
+ attribute.Int("failure_len", len(r.GetFailure())),
+ attribute.Bool("read_only", readOnly),
+ ))
+ defer span.End()
+
+ ctx, trace := traceutil.EnsureTrace(ctx, s.Logger(), "transaction",
+ traceutil.Field{Key: "read_only", Value: readOnly},
+ )
+ if readOnly {
if !txn.IsTxnSerializable(r) {
- err := s.linearizableReadNotify(ctx)
+ err := s.read.LinearizableReadNotify(ctx)
trace.Step("agreement among raft nodes before linearized reading")
if err != nil {
return nil, err
@@ -174,16 +352,18 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse
var resp *pb.TxnResponse
var err error
chk := func(ai *auth.AuthInfo) error {
- return txn.CheckTxnAuth(s.authStore, ai, r)
+ return apply2.CheckTxnAuth(s.authStore, ai, s.lessor, r)
}
defer func(start time.Time) {
txn.WarnOfExpensiveReadOnlyTxnRequest(s.Logger(), s.Cfg.WarningApplyDuration, start, r, resp, err)
trace.LogIfLong(traceThreshold)
+ success := err == nil
+ requestDurationSec.WithLabelValues("ReadonlyTxn", strconv.FormatBool(success)).Observe(time.Since(start).Seconds())
}(time.Now())
get := func() {
- resp, _, err = txn.Txn(ctx, s.Logger(), r, s.Cfg.ServerFeatureGate.Enabled(features.TxnModeWriteWithSharedBuffer), s.KV(), s.lessor)
+ resp, _, err = txn.Txn(ctx, s.Logger(), r, s.Cfg.ServerFeatureGate.Enabled(features.TxnModeWriteWithSharedBuffer), s.KV(), s.lessor, false)
}
if serr := s.doSerialize(ctx, chk, get); serr != nil {
return nil, serr
@@ -192,7 +372,7 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse
}
ctx = context.WithValue(ctx, traceutil.StartTimeKey{}, time.Now())
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{Txn: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{Txn: r})
if err != nil {
return nil, err
}
@@ -200,9 +380,16 @@ func (s *EtcdServer) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse
}
func (s *EtcdServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "compact", trace.WithAttributes(
+ attribute.Bool("is_physical", r.GetPhysical()),
+ attribute.Int64("rev", r.GetRevision()),
+ ))
+ defer span.End()
+
startTime := time.Now()
- result, err := s.processInternalRaftRequestOnce(ctx, pb.InternalRaftRequest{Compaction: r})
- trace := traceutil.TODO()
+ ctx, trace := traceutil.EnsureTrace(ctx, s.Logger(), "compact")
+ result, err := s.processInternalRaftRequestOnce(ctx, &pb.InternalRaftRequest{Compaction: r})
if result != nil && result.Trace != nil {
trace = result.Trace
defer func() {
@@ -250,12 +437,18 @@ func (s *EtcdServer) LeaseGrant(ctx context.Context, r *pb.LeaseGrantRequest) (*
// only use positive int64 id's
r.ID = int64(s.reqIDGen.Next() & ((1 << 63) - 1))
}
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "lease_grant", trace.WithAttributes(
+ attribute.Int64("id", r.ID),
+ attribute.Int64("ttl", r.GetTTL()),
+ ))
+ defer span.End()
if err := s.requireAuthInfo(ctx); err != nil {
return nil, err
}
- resp, err := s.raftRequestOnce(ctx, pb.InternalRaftRequest{LeaseGrant: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{LeaseGrant: r})
if err != nil {
return nil, err
}
@@ -264,7 +457,7 @@ func (s *EtcdServer) LeaseGrant(ctx context.Context, r *pb.LeaseGrantRequest) (*
func (s *EtcdServer) waitAppliedIndex() error {
select {
- case <-s.ApplyWait():
+ case <-s.ApplyWaitCommit():
case <-s.stopping:
return errors.ErrStopped
case <-time.After(applyTimeout):
@@ -275,11 +468,17 @@ func (s *EtcdServer) waitAppliedIndex() error {
}
func (s *EtcdServer) LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest) (*pb.LeaseRevokeResponse, error) {
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "lease_revoke", trace.WithAttributes(
+ attribute.Int64("id", r.GetID()),
+ ))
+ defer span.End()
+
if err := s.requireAuthInfo(ctx); err != nil {
return nil, err
}
- resp, err := s.raftRequestOnce(ctx, pb.InternalRaftRequest{LeaseRevoke: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{LeaseRevoke: r})
if err != nil {
return nil, err
}
@@ -287,6 +486,12 @@ func (s *EtcdServer) LeaseRevoke(ctx context.Context, r *pb.LeaseRevokeRequest)
}
func (s *EtcdServer) LeaseRenew(ctx context.Context, id lease.LeaseID) (int64, error) {
+ var span trace.Span
+ ctx, span = traceutil.Tracer.Start(ctx, "lease_renew", trace.WithAttributes(
+ attribute.Int64("id", int64(id)),
+ ))
+ defer span.End()
+
if s.isLeader() {
// If s.isLeader() returns true, but we fail to ensure the current
// member's leadership, there are a couple of possibilities:
@@ -298,8 +503,22 @@ func (s *EtcdServer) LeaseRenew(ctx context.Context, id lease.LeaseID) (int64, e
if !s.ensureLeadership() {
return -1, lease.ErrNotPrimary
}
- if err := s.waitAppliedIndex(); err != nil {
- return 0, err
+
+ // This change aims to make lease renewal faster under high server load
+ // while preserving correctness. If a lease is not found, it might still be in
+ // the process of being created. We must wait for the applied index to advance
+ // to verify whether the lease truly does not exist.
+ if s.FeatureEnabled(features.FastLeaseKeepAlive) {
+ le := s.lessor.Lookup(id)
+ if le == nil {
+ if err := s.waitAppliedIndex(); err != nil {
+ return 0, err
+ }
+ }
+ } else {
+ if err := s.waitAppliedIndex(); err != nil {
+ return 0, err
+ }
}
if err := s.checkLeaseRenew(ctx, id); err != nil {
@@ -340,10 +559,16 @@ func (s *EtcdServer) LeaseRenew(ctx context.Context, id lease.LeaseID) (int64, e
time.Sleep(50 * time.Millisecond)
}
- if errorspkg.Is(cctx.Err(), context.DeadlineExceeded) {
+ err := cctx.Err()
+ switch {
+ case errorspkg.Is(err, context.DeadlineExceeded):
return -1, errors.ErrTimeout
+ case errorspkg.Is(err, context.Canceled):
+ return -1, errors.ErrCanceled
+ default:
+ s.Logger().Warn("Unexpected lease renew context error", zap.Error(err))
+ return -1, errors.ErrCanceled
}
- return -1, errors.ErrCanceled
}
func (s *EtcdServer) checkLeaseRenew(ctx context.Context, leaseID lease.LeaseID) error {
@@ -577,7 +802,7 @@ func (s *EtcdServer) waitLeader(ctx context.Context) (*membership.Member, error)
}
func (s *EtcdServer) Alarm(ctx context.Context, r *pb.AlarmRequest) (*pb.AlarmResponse, error) {
- resp, err := s.raftRequestOnce(ctx, pb.InternalRaftRequest{Alarm: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{Alarm: r})
if err != nil {
return nil, err
}
@@ -585,7 +810,7 @@ func (s *EtcdServer) Alarm(ctx context.Context, r *pb.AlarmRequest) (*pb.AlarmRe
}
func (s *EtcdServer) AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*pb.AuthEnableResponse, error) {
- resp, err := s.raftRequestOnce(ctx, pb.InternalRaftRequest{AuthEnable: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthEnable: r})
if err != nil {
return nil, err
}
@@ -593,7 +818,7 @@ func (s *EtcdServer) AuthEnable(ctx context.Context, r *pb.AuthEnableRequest) (*
}
func (s *EtcdServer) AuthDisable(ctx context.Context, r *pb.AuthDisableRequest) (*pb.AuthDisableResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthDisable: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthDisable: r})
if err != nil {
return nil, err
}
@@ -601,7 +826,7 @@ func (s *EtcdServer) AuthDisable(ctx context.Context, r *pb.AuthDisableRequest)
}
func (s *EtcdServer) AuthStatus(ctx context.Context, r *pb.AuthStatusRequest) (*pb.AuthStatusResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthStatus: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthStatus: r})
if err != nil {
return nil, err
}
@@ -609,7 +834,7 @@ func (s *EtcdServer) AuthStatus(ctx context.Context, r *pb.AuthStatusRequest) (*
}
func (s *EtcdServer) Authenticate(ctx context.Context, r *pb.AuthenticateRequest) (*pb.AuthenticateResponse, error) {
- if err := s.linearizableReadNotify(ctx); err != nil {
+ if err := s.read.LinearizableReadNotify(ctx); err != nil {
return nil, err
}
@@ -648,7 +873,7 @@ func (s *EtcdServer) Authenticate(ctx context.Context, r *pb.AuthenticateRequest
SimpleToken: st,
}
- resp, err = s.raftRequestOnce(ctx, pb.InternalRaftRequest{Authenticate: internalReq})
+ resp, err = s.raftRequest(ctx, &pb.InternalRaftRequest{Authenticate: internalReq})
if err != nil {
return nil, err
}
@@ -672,7 +897,7 @@ func (s *EtcdServer) UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb
r.Password = ""
}
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserAdd: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserAdd: r})
if err != nil {
return nil, err
}
@@ -680,7 +905,7 @@ func (s *EtcdServer) UserAdd(ctx context.Context, r *pb.AuthUserAddRequest) (*pb
}
func (s *EtcdServer) UserDelete(ctx context.Context, r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserDelete: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserDelete: r})
if err != nil {
return nil, err
}
@@ -697,7 +922,7 @@ func (s *EtcdServer) UserChangePassword(ctx context.Context, r *pb.AuthUserChang
r.Password = ""
}
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserChangePassword: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserChangePassword: r})
if err != nil {
return nil, err
}
@@ -705,7 +930,7 @@ func (s *EtcdServer) UserChangePassword(ctx context.Context, r *pb.AuthUserChang
}
func (s *EtcdServer) UserGrantRole(ctx context.Context, r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserGrantRole: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserGrantRole: r})
if err != nil {
return nil, err
}
@@ -713,7 +938,7 @@ func (s *EtcdServer) UserGrantRole(ctx context.Context, r *pb.AuthUserGrantRoleR
}
func (s *EtcdServer) UserGet(ctx context.Context, r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserGet: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserGet: r})
if err != nil {
return nil, err
}
@@ -721,7 +946,7 @@ func (s *EtcdServer) UserGet(ctx context.Context, r *pb.AuthUserGetRequest) (*pb
}
func (s *EtcdServer) UserList(ctx context.Context, r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserList: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserList: r})
if err != nil {
return nil, err
}
@@ -729,7 +954,7 @@ func (s *EtcdServer) UserList(ctx context.Context, r *pb.AuthUserListRequest) (*
}
func (s *EtcdServer) UserRevokeRole(ctx context.Context, r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthUserRevokeRole: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthUserRevokeRole: r})
if err != nil {
return nil, err
}
@@ -737,7 +962,7 @@ func (s *EtcdServer) UserRevokeRole(ctx context.Context, r *pb.AuthUserRevokeRol
}
func (s *EtcdServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleAdd: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleAdd: r})
if err != nil {
return nil, err
}
@@ -745,7 +970,7 @@ func (s *EtcdServer) RoleAdd(ctx context.Context, r *pb.AuthRoleAddRequest) (*pb
}
func (s *EtcdServer) RoleGrantPermission(ctx context.Context, r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleGrantPermission: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleGrantPermission: r})
if err != nil {
return nil, err
}
@@ -753,7 +978,7 @@ func (s *EtcdServer) RoleGrantPermission(ctx context.Context, r *pb.AuthRoleGran
}
func (s *EtcdServer) RoleGet(ctx context.Context, r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleGet: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleGet: r})
if err != nil {
return nil, err
}
@@ -761,7 +986,7 @@ func (s *EtcdServer) RoleGet(ctx context.Context, r *pb.AuthRoleGetRequest) (*pb
}
func (s *EtcdServer) RoleList(ctx context.Context, r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleList: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleList: r})
if err != nil {
return nil, err
}
@@ -769,7 +994,7 @@ func (s *EtcdServer) RoleList(ctx context.Context, r *pb.AuthRoleListRequest) (*
}
func (s *EtcdServer) RoleRevokePermission(ctx context.Context, r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleRevokePermission: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleRevokePermission: r})
if err != nil {
return nil, err
}
@@ -777,16 +1002,17 @@ func (s *EtcdServer) RoleRevokePermission(ctx context.Context, r *pb.AuthRoleRev
}
func (s *EtcdServer) RoleDelete(ctx context.Context, r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) {
- resp, err := s.raftRequest(ctx, pb.InternalRaftRequest{AuthRoleDelete: r})
+ resp, err := s.raftRequest(ctx, &pb.InternalRaftRequest{AuthRoleDelete: r})
if err != nil {
return nil, err
}
return resp.(*pb.AuthRoleDeleteResponse), nil
}
-func (s *EtcdServer) raftRequestOnce(ctx context.Context, r pb.InternalRaftRequest) (proto.Message, error) {
+func (s *EtcdServer) raftRequest(ctx context.Context, r *pb.InternalRaftRequest) (proto.Message, error) {
result, err := s.processInternalRaftRequestOnce(ctx, r)
if err != nil {
+ trace.SpanFromContext(ctx).RecordError(err)
return nil, err
}
if result.Err != nil {
@@ -804,10 +1030,6 @@ func (s *EtcdServer) raftRequestOnce(ctx context.Context, r pb.InternalRaftReque
return result.Resp, nil
}
-func (s *EtcdServer) raftRequest(ctx context.Context, r pb.InternalRaftRequest) (proto.Message, error) {
- return s.raftRequestOnce(ctx, r)
-}
-
// doSerialize handles the auth logic, with permissions checked by "chk", for a serialized request "get". Returns a non-nil error on authentication failure.
func (s *EtcdServer) doSerialize(ctx context.Context, chk func(*auth.AuthInfo) error, get func()) error {
trace := traceutil.Get(ctx)
@@ -833,10 +1055,11 @@ func (s *EtcdServer) doSerialize(ctx context.Context, chk func(*auth.AuthInfo) e
return nil
}
-func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.InternalRaftRequest) (*apply2.Result, error) {
+func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r *pb.InternalRaftRequest) (*apply2.Result, error) {
ai := s.getAppliedIndex()
ci := s.getCommittedIndex()
- if ci > ai+maxGapBetweenApplyAndCommitIndex {
+
+ if exceedsRequestLimit(ai, ci, r, s.FeatureEnabled(features.PriorityRequest)) {
return nil, errors.ErrTooManyRequests
}
@@ -856,7 +1079,18 @@ func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.In
}
}
- data, err := r.Marshal()
+ var (
+ data []byte
+ err error
+ start = time.Now()
+ reqType = getRequestType(r)
+ )
+ defer func() {
+ success := err == nil
+ requestDurationSec.WithLabelValues(reqType, strconv.FormatBool(success)).Observe(time.Since(start).Seconds())
+ }()
+
+ data, err = proto.Marshal(r)
if err != nil {
return nil, err
}
@@ -874,7 +1108,8 @@ func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.In
cctx, cancel := context.WithTimeout(ctx, s.Cfg.ReqTimeout())
defer cancel()
- start := time.Now()
+ span := trace.SpanFromContext(ctx)
+ span.AddEvent("Send raft proposal")
err = s.r.Propose(cctx, data)
if err != nil {
proposalsFailed.Inc()
@@ -886,6 +1121,7 @@ func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.In
select {
case x := <-ch:
+ span.AddEvent("Receive raft result")
return x.(*apply2.Result), nil
case <-cctx.Done():
proposalsFailed.Inc()
@@ -896,199 +1132,76 @@ func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.In
}
}
-// Watchable returns a watchable interface attached to the etcdserver.
-func (s *EtcdServer) Watchable() mvcc.WatchableKV { return s.KV() }
-
-func (s *EtcdServer) linearizableReadLoop() {
- for {
- leaderChangedNotifier := s.leaderChanged.Receive()
- select {
- case <-leaderChangedNotifier:
- continue
- case <-s.readwaitc:
- case <-s.stopping:
- return
- }
-
- // as a single loop is can unlock multiple reads, it is not very useful
- // to propagate the trace from Txn or Range.
- trace := traceutil.New("linearizableReadLoop", s.Logger())
-
- nextnr := newNotifier()
- s.readMu.Lock()
- nr := s.readNotifier
- s.readNotifier = nextnr
- s.readMu.Unlock()
-
- confirmedIndex, err := s.requestCurrentIndex(leaderChangedNotifier)
- if isStopped(err) {
- return
- }
- if err != nil {
- nr.notify(err)
- continue
- }
-
- trace.Step("read index received")
-
- trace.AddField(traceutil.Field{Key: "readStateIndex", Value: confirmedIndex})
-
- appliedIndex := s.getAppliedIndex()
- trace.AddField(traceutil.Field{Key: "appliedIndex", Value: strconv.FormatUint(appliedIndex, 10)})
-
- if appliedIndex < confirmedIndex {
- select {
- case <-s.applyWait.Wait(confirmedIndex):
- case <-s.stopping:
- return
- }
- }
- // unblock all l-reads requested at indices before confirmedIndex
- nr.notify(nil)
- trace.Step("applied index is now lower than readState.Index")
-
- trace.LogAllStepsIfLong(traceThreshold)
- }
-}
-
-func isStopped(err error) bool {
- return errorspkg.Is(err, raft.ErrStopped) || errorspkg.Is(err, errors.ErrStopped)
-}
-
-func (s *EtcdServer) requestCurrentIndex(leaderChangedNotifier <-chan struct{}) (uint64, error) {
- requestIDs := map[uint64]struct{}{}
- requestID := s.reqIDGen.Next()
- requestIDs[requestID] = struct{}{}
- err := s.sendReadIndex(requestID)
- if err != nil {
- return 0, err
- }
-
- lg := s.Logger()
- errorTimer := time.NewTimer(s.Cfg.ReqTimeout())
- defer errorTimer.Stop()
- retryTimer := time.NewTimer(readIndexRetryTime)
- defer retryTimer.Stop()
-
- firstCommitInTermNotifier := s.firstCommitInTerm.Receive()
-
- for {
- select {
- case rs := <-s.r.readStateC:
- // Check again if leader changed as when multiple channels are ready, select picks randomly.
- select {
- case <-leaderChangedNotifier:
- readIndexFailed.Inc()
- return 0, errors.ErrLeaderChanged
- default:
- }
- responseID := uint64(0)
- if len(rs.RequestCtx) == 8 {
- responseID = binary.BigEndian.Uint64(rs.RequestCtx)
- }
- if _, ok := requestIDs[responseID]; !ok {
- // a previous request might time out. now we should ignore the response of it and
- // continue waiting for the response of the current requests.
- lg.Warn(
- "ignored out-of-date read index response; local node read indexes queueing up and waiting to be in sync with leader",
- zap.Uint64("received-request-id", responseID),
- )
- slowReadIndex.Inc()
- continue
- }
- return rs.Index, nil
- case <-leaderChangedNotifier:
- readIndexFailed.Inc()
- // return a retryable error.
- return 0, errors.ErrLeaderChanged
- case <-firstCommitInTermNotifier:
- firstCommitInTermNotifier = s.firstCommitInTerm.Receive()
- lg.Info("first commit in current term: resending ReadIndex request")
- requestID = s.reqIDGen.Next()
- requestIDs[requestID] = struct{}{}
- err := s.sendReadIndex(requestID)
- if err != nil {
- return 0, err
- }
- retryTimer.Reset(readIndexRetryTime)
- continue
- case <-retryTimer.C:
- lg.Warn(
- "waiting for ReadIndex response took too long, retrying",
- zap.Uint64("sent-request-id", requestID),
- zap.Duration("retry-timeout", readIndexRetryTime),
- )
- requestID = s.reqIDGen.Next()
- requestIDs[requestID] = struct{}{}
- err := s.sendReadIndex(requestID)
- if err != nil {
- return 0, err
- }
- retryTimer.Reset(readIndexRetryTime)
- continue
- case <-errorTimer.C:
- lg.Warn(
- "timed out waiting for read index response (local node might have slow network)",
- zap.Duration("timeout", s.Cfg.ReqTimeout()),
- )
- slowReadIndex.Inc()
- return 0, errors.ErrTimeout
- case <-s.stopping:
- return 0, errors.ErrStopped
- }
- }
-}
-
-func uint64ToBigEndianBytes(number uint64) []byte {
- byteResult := make([]byte, 8)
- binary.BigEndian.PutUint64(byteResult, number)
- return byteResult
-}
-
-func (s *EtcdServer) sendReadIndex(requestIndex uint64) error {
- ctxToSend := uint64ToBigEndianBytes(requestIndex)
-
- cctx, cancel := context.WithTimeout(context.Background(), s.Cfg.ReqTimeout())
- err := s.r.ReadIndex(cctx, ctxToSend)
- cancel()
- if errorspkg.Is(err, raft.ErrStopped) {
- return err
- }
- if err != nil {
- lg := s.Logger()
- lg.Warn("failed to get read index from Raft", zap.Error(err))
- readIndexFailed.Inc()
- return err
- }
- return nil
-}
-
-func (s *EtcdServer) LinearizableReadNotify(ctx context.Context) error {
- return s.linearizableReadNotify(ctx)
-}
-
-func (s *EtcdServer) linearizableReadNotify(ctx context.Context) error {
- s.readMu.RLock()
- nc := s.readNotifier
- s.readMu.RUnlock()
-
- // signal linearizable loop for current notify if it hasn't been already
- select {
- case s.readwaitc <- struct{}{}:
+func getRequestType(r *pb.InternalRaftRequest) string {
+ switch {
+ case r.Range != nil:
+ return "Range"
+ case r.Put != nil:
+ return "Put"
+ case r.DeleteRange != nil:
+ return "DeleteRange"
+ case r.Txn != nil:
+ return "Txn"
+ case r.Compaction != nil:
+ return "Compaction"
+ case r.LeaseGrant != nil:
+ return "LeaseGrant"
+ case r.LeaseRevoke != nil:
+ return "LeaseRevoke"
+ case r.LeaseCheckpoint != nil:
+ return "LeaseCheckpoint"
+ case r.Alarm != nil:
+ return "Alarm"
+ case r.Authenticate != nil:
+ return "Authenticate"
+ case r.AuthEnable != nil:
+ return "AuthEnable"
+ case r.AuthDisable != nil:
+ return "AuthDisable"
+ case r.AuthStatus != nil:
+ return "AuthStatus"
+ case r.AuthUserAdd != nil:
+ return "AuthUserAdd"
+ case r.AuthUserDelete != nil:
+ return "AuthUserDelete"
+ case r.AuthUserChangePassword != nil:
+ return "AuthUserChangePassword"
+ case r.AuthUserGrantRole != nil:
+ return "AuthUserGrantRole"
+ case r.AuthUserGet != nil:
+ return "AuthUserGet"
+ case r.AuthUserRevokeRole != nil:
+ return "AuthUserRevokeRole"
+ case r.AuthRoleAdd != nil:
+ return "AuthRoleAdd"
+ case r.AuthRoleGrantPermission != nil:
+ return "AuthRoleGrantPermission"
+ case r.AuthRoleGet != nil:
+ return "AuthRoleGet"
+ case r.AuthRoleRevokePermission != nil:
+ return "AuthRoleRevokePermission"
+ case r.AuthRoleDelete != nil:
+ return "AuthRoleDelete"
+ case r.AuthUserList != nil:
+ return "AuthUserList"
+ case r.AuthRoleList != nil:
+ return "AuthRoleList"
+ case r.ClusterVersionSet != nil:
+ return "ClusterVersionSet"
+ case r.ClusterMemberAttrSet != nil:
+ return "ClusterMemberAttrSet"
+ case r.DowngradeInfoSet != nil:
+ return "DowngradeInfoSet"
+ case r.DowngradeVersionTest != nil:
+ return "DowngradeVersionTest"
default:
- }
-
- // wait for read state notification
- select {
- case <-nc.c:
- return nc.err
- case <-ctx.Done():
- return ctx.Err()
- case <-s.done:
- return errors.ErrStopped
+ return "Unknown"
}
}
+// Watchable returns a watchable interface attached to the etcdserver.
+func (s *EtcdServer) Watchable() mvcc.WatchableKV { return s.KV() }
+
func (s *EtcdServer) AuthInfoFromCtx(ctx context.Context) (*auth.AuthInfo, error) {
authInfo, err := s.AuthStore().AuthInfoFromCtx(ctx)
if authInfo != nil || err != nil {
diff --git a/server/etcdserver/version/version_test.go b/server/etcdserver/version/version_test.go
index cae8e70a3b5b..3d5570352900 100644
--- a/server/etcdserver/version/version_test.go
+++ b/server/etcdserver/version/version_test.go
@@ -65,7 +65,7 @@ func TestDowngradeSingleNode(t *testing.T) {
c.StepMonitors()
assert.Equal(t, newCluster(lg, 1, version.V3_6), c)
- require.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5))
+ require.NoError(t, c.Version().DowngradeEnable(t.Context(), &version.V3_5))
c.StepMonitors()
assert.Equal(t, version.V3_5, c.clusterVersion)
@@ -81,7 +81,7 @@ func TestDowngradeThreeNode(t *testing.T) {
c.StepMonitors()
assert.Equal(t, newCluster(lg, 3, version.V3_6), c)
- require.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5))
+ require.NoError(t, c.Version().DowngradeEnable(t.Context(), &version.V3_5))
c.StepMonitors()
assert.Equal(t, version.V3_5, c.clusterVersion)
@@ -101,7 +101,7 @@ func TestNewerMemberCanReconnectDuringDowngrade(t *testing.T) {
c.StepMonitors()
assert.Equal(t, newCluster(lg, 3, version.V3_6), c)
- require.NoError(t, c.Version().DowngradeEnable(context.Background(), &version.V3_5))
+ require.NoError(t, c.Version().DowngradeEnable(t.Context(), &version.V3_5))
c.StepMonitors()
assert.Equal(t, version.V3_5, c.clusterVersion)
diff --git a/server/features/etcd_features.go b/server/features/etcd_features.go
index 230b37c703a3..6ff85e35779d 100644
--- a/server/features/etcd_features.go
+++ b/server/features/etcd_features.go
@@ -76,28 +76,15 @@ const (
SetMemberLocalAddr featuregate.Feature = "SetMemberLocalAddr"
)
-var (
- DefaultEtcdServerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
- StopGRPCServiceOnDefrag: {Default: false, PreRelease: featuregate.Alpha},
- InitialCorruptCheck: {Default: false, PreRelease: featuregate.Alpha},
- CompactHashCheck: {Default: false, PreRelease: featuregate.Alpha},
- TxnModeWriteWithSharedBuffer: {Default: true, PreRelease: featuregate.Beta},
- LeaseCheckpoint: {Default: false, PreRelease: featuregate.Alpha},
- LeaseCheckpointPersist: {Default: false, PreRelease: featuregate.Alpha},
- SetMemberLocalAddr: {Default: false, PreRelease: featuregate.Alpha},
- }
- // ExperimentalFlagToFeatureMap is the map from the cmd line flags of experimental features
- // to their corresponding feature gates.
- // Deprecated: Only add existing experimental features here. DO NOT use for new features.
- ExperimentalFlagToFeatureMap = map[string]featuregate.Feature{
- "experimental-stop-grpc-service-on-defrag": StopGRPCServiceOnDefrag,
- "experimental-initial-corrupt-check": InitialCorruptCheck,
- "experimental-compact-hash-check-enabled": CompactHashCheck,
- "experimental-txn-mode-write-with-shared-buffer": TxnModeWriteWithSharedBuffer,
- "experimental-enable-lease-checkpoint": LeaseCheckpoint,
- "experimental-enable-lease-checkpoint-persist": LeaseCheckpointPersist,
- }
-)
+var DefaultEtcdServerFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
+ StopGRPCServiceOnDefrag: {Default: false, PreRelease: featuregate.Alpha},
+ InitialCorruptCheck: {Default: false, PreRelease: featuregate.Alpha},
+ CompactHashCheck: {Default: false, PreRelease: featuregate.Alpha},
+ TxnModeWriteWithSharedBuffer: {Default: true, PreRelease: featuregate.Beta},
+ LeaseCheckpoint: {Default: false, PreRelease: featuregate.Alpha},
+ LeaseCheckpointPersist: {Default: false, PreRelease: featuregate.Alpha},
+ SetMemberLocalAddr: {Default: false, PreRelease: featuregate.Alpha},
+}
func NewDefaultServerFeatureGate(name string, lg *zap.Logger) featuregate.FeatureGate {
fg := featuregate.New(fmt.Sprintf("%sServerFeatureGate", name), lg)
diff --git a/server/go.mod b/server/go.mod
index d98515ef387f..ac2bac72b85f 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -1,80 +1,76 @@
module go.etcd.io/etcd/server/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/coreos/go-semver v0.3.1
- github.com/coreos/go-systemd/v22 v22.5.0
+ github.com/coreos/go-systemd/v22 v22.7.0
github.com/dustin/go-humanize v1.0.1
- github.com/gogo/protobuf v1.3.2
- github.com/golang-jwt/jwt/v5 v5.2.2
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
+ github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang/protobuf v1.5.4
- github.com/google/btree v1.1.3
github.com/google/go-cmp v0.7.0
- github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
+ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
github.com/jonboulle/clockwork v0.5.0
- github.com/prometheus/client_golang v1.20.5
- github.com/prometheus/client_model v0.6.1
+ github.com/prometheus/client_golang v1.23.2
+ github.com/prometheus/client_model v0.6.2
github.com/soheilhy/cmux v0.1.5
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
- github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802
- github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2
- go.etcd.io/bbolt v1.4.3
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.etcd.io/etcd/client/v3 v3.6.10
- go.etcd.io/etcd/pkg/v3 v3.6.10
- go.etcd.io/raft/v3 v3.6.0
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
- go.opentelemetry.io/otel v1.40.0
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0
- go.opentelemetry.io/otel/sdk v1.40.0
- go.uber.org/zap v1.27.0
- golang.org/x/crypto v0.48.0
- golang.org/x/net v0.51.0
- golang.org/x/time v0.9.0
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217
- google.golang.org/grpc v1.79.3
- google.golang.org/protobuf v1.36.10
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75
+ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510
+ go.etcd.io/bbolt v1.5.0
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ go.etcd.io/etcd/pkg/v3 v3.7.0
+ go.etcd.io/raft/v3 v3.7.0
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
+ go.opentelemetry.io/otel v1.43.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
+ go.opentelemetry.io/otel/sdk v1.43.0
+ go.opentelemetry.io/otel/trace v1.43.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/crypto v0.52.0
+ golang.org/x/net v0.55.0
+ golang.org/x/time v0.15.0
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
gopkg.in/natefinch/lumberjack.v2 v2.2.1
- sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6
- sigs.k8s.io/yaml v1.4.0
+ k8s.io/utils v0.0.0-20260108192941-914a6e750570
+ sigs.k8s.io/yaml v1.6.0
)
require (
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/common v0.62.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/sirupsen/logrus v1.9.4 // indirect
github.com/spf13/pflag v1.0.10 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
- go.opentelemetry.io/otel/metric v1.40.0 // indirect
- go.opentelemetry.io/otel/trace v1.40.0 // indirect
- go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
+ go.opentelemetry.io/otel/metric v1.43.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -84,9 +80,3 @@ replace (
go.etcd.io/etcd/client/v3 => ../client/v3
go.etcd.io/etcd/pkg/v3 => ../pkg
)
-
-// Bad imports are sometimes causing attempts to pull that code.
-// This makes the error more explicit.
-replace go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
-
-replace go.etcd.io/tests/v3 => ./FORBIDDEN_DEPENDENCY
diff --git a/server/go.sum b/server/go.sum
index bedd9f43de4c..dd8c7f875ea4 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -1,80 +1,50 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
+github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -83,25 +53,23 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
@@ -110,140 +78,96 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
-go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
-go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
+go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
+go.etcd.io/raft/v3 v3.7.0 h1:BGzlwx07bLv8PW6OU5HObuz1y4hlPZUXA07pM1mPUh4=
+go.etcd.io/raft/v3 v3.7.0/go.mod h1:6gX6T2X907DjnjsFLODnTxba77stjs84W9gTTI0GUNA=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
-go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
-go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
-go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
-go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
-go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
-go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
-go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
-go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
-go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
-go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
-go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
+go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/server/internal/clientv2/client_test.go b/server/internal/clientv2/client_test.go
index 19374ec93624..66e5671a3f7b 100644
--- a/server/internal/clientv2/client_test.go
+++ b/server/internal/clientv2/client_test.go
@@ -131,7 +131,7 @@ func TestSimpleHTTPClientDoSuccess(t *testing.T) {
Body: io.NopCloser(strings.NewReader("foo")),
}
- resp, body, err := c.Do(context.Background(), &fakeAction{})
+ resp, body, err := c.Do(t.Context(), &fakeAction{})
require.NoErrorf(t, err, "incorrect error value")
wantCode := http.StatusTeapot
require.Equalf(t, wantCode, resp.StatusCode, "invalid response code: want=%d got=%d", wantCode, resp.StatusCode)
@@ -146,7 +146,7 @@ func TestSimpleHTTPClientDoError(t *testing.T) {
tr.errchan <- errors.New("fixture")
- _, _, err := c.Do(context.Background(), &fakeAction{})
+ _, _, err := c.Do(t.Context(), &fakeAction{})
assert.Errorf(t, err, "expected non-nil error, got nil")
}
@@ -162,7 +162,7 @@ func TestSimpleHTTPClientDoNilRequest(t *testing.T) {
tr.errchan <- errors.New("fixture")
- _, _, err := c.Do(context.Background(), &nilAction{})
+ _, _, err := c.Do(t.Context(), &nilAction{})
require.ErrorIsf(t, err, ErrNoRequest, "expected non-nil error, got nil")
}
@@ -173,7 +173,7 @@ func TestSimpleHTTPClientDoCancelContext(t *testing.T) {
tr.startCancel <- struct{}{}
tr.finishCancel <- struct{}{}
- _, _, err := c.Do(context.Background(), &fakeAction{})
+ _, _, err := c.Do(t.Context(), &fakeAction{})
assert.Errorf(t, err, "expected non-nil error, got nil")
}
@@ -195,7 +195,7 @@ func TestSimpleHTTPClientDoCancelContextResponseBodyClosed(t *testing.T) {
c := &simpleHTTPClient{transport: tr}
// create an already-cancelled context
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
body := &checkableReadCloser{ReadCloser: io.NopCloser(strings.NewReader("foo"))}
@@ -232,7 +232,7 @@ func TestSimpleHTTPClientDoCancelContextResponseBodyClosedWithBlockingBody(t *te
tr := newFakeTransport()
c := &simpleHTTPClient{transport: tr}
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
body := &checkableReadCloser{ReadCloser: &blockingBody{c: make(chan struct{})}}
go func() {
tr.respchan <- &http.Response{Body: body}
@@ -252,7 +252,7 @@ func TestSimpleHTTPClientDoCancelContextWaitForRoundTrip(t *testing.T) {
c := &simpleHTTPClient{transport: tr}
donechan := make(chan struct{})
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
go func() {
c.Do(ctx, &fakeAction{})
close(donechan)
@@ -285,7 +285,7 @@ func TestSimpleHTTPClientDoHeaderTimeout(t *testing.T) {
errc := make(chan error, 1)
go func() {
- _, _, err := c.Do(context.Background(), &fakeAction{})
+ _, _, err := c.Do(t.Context(), &fakeAction{})
errc <- err
}()
@@ -407,7 +407,7 @@ func TestHTTPClusterClientDo(t *testing.T) {
),
rand: rand.New(rand.NewSource(0)),
},
- ctx: context.WithValue(context.Background(), &oneShotCtxValue, &oneShotCtxValue),
+ ctx: context.WithValue(t.Context(), &oneShotCtxValue, &oneShotCtxValue),
wantErr: errors.New("client: etcd member returns server error [Bad Gateway]"),
wantPinned: 1,
},
@@ -415,7 +415,7 @@ func TestHTTPClusterClientDo(t *testing.T) {
for i, tt := range tests {
if tt.ctx == nil {
- tt.ctx = context.Background()
+ tt.ctx = t.Context()
}
resp, _, err := tt.client.Do(tt.ctx, nil)
if (tt.wantErr == nil && !errors.Is(err, tt.wantErr)) || (tt.wantErr != nil && tt.wantErr.Error() != err.Error()) {
@@ -450,7 +450,7 @@ func TestHTTPClusterClientDoDeadlineExceedContext(t *testing.T) {
errc := make(chan error, 1)
go func() {
- ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Millisecond)
defer cancel()
_, _, err := c.Do(ctx, &fakeAction{})
errc <- err
@@ -722,7 +722,7 @@ func TestRedirectFollowingHTTPClient(t *testing.T) {
for i, tt := range tests {
client := &redirectFollowingHTTPClient{client: tt.client, checkRedirect: tt.checkRedirect}
- resp, _, err := client.Do(context.Background(), nil)
+ resp, _, err := client.Do(t.Context(), nil)
if (tt.wantErr == nil && !errors.Is(err, tt.wantErr)) || (tt.wantErr != nil && tt.wantErr.Error() != err.Error()) {
t.Errorf("#%d: got err=%v, want=%v", i, err, tt.wantErr)
continue
@@ -781,7 +781,7 @@ func TestHTTPClusterClientSync(t *testing.T) {
got := hc.Endpoints()
require.Truef(t, reflect.DeepEqual(want, got), "incorrect endpoints: want=%#v got=%#v", want, got)
- err = hc.Sync(context.Background())
+ err = hc.Sync(t.Context())
require.NoErrorf(t, err, "unexpected error during Sync: %#v", err)
want = []string{"http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002", "http://127.0.0.1:4003"}
@@ -813,7 +813,7 @@ func TestHTTPClusterClientSyncFail(t *testing.T) {
got := hc.Endpoints()
require.Truef(t, reflect.DeepEqual(want, got), "incorrect endpoints: want=%#v got=%#v", want, got)
- err = hc.Sync(context.Background())
+ err = hc.Sync(t.Context())
require.Errorf(t, err, "got nil error during Sync")
got = hc.Endpoints()
@@ -835,7 +835,7 @@ func TestHTTPClusterClientAutoSyncCancelContext(t *testing.T) {
err := hc.SetEndpoints([]string{"http://127.0.0.1:2379"})
require.NoErrorf(t, err, "unexpected error during setup")
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
err = hc.AutoSync(ctx, time.Hour)
@@ -854,7 +854,7 @@ func TestHTTPClusterClientAutoSyncFail(t *testing.T) {
err := hc.SetEndpoints([]string{"http://127.0.0.1:2379"})
require.NoErrorf(t, err, "unexpected error during setup")
- err = hc.AutoSync(context.Background(), time.Hour)
+ err = hc.AutoSync(t.Context(), time.Hour)
require.Truef(t, strings.HasPrefix(err.Error(), ErrClusterUnavailable.Error()), "incorrect error value: want=%v got=%v", ErrClusterUnavailable, err)
}
@@ -874,7 +874,7 @@ func TestHTTPClusterClientGetVersion(t *testing.T) {
err := hc.SetEndpoints([]string{"http://127.0.0.1:4003", "http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002"})
require.NoErrorf(t, err, "unexpected error during setup")
- actual, err := hc.GetVersion(context.Background())
+ actual, err := hc.GetVersion(t.Context())
if err != nil {
t.Errorf("non-nil error: %#v", err)
}
@@ -911,7 +911,7 @@ func TestHTTPClusterClientSyncPinEndpoint(t *testing.T) {
pinnedEndpoint := hc.endpoints[hc.pinned]
for i := 0; i < 3; i++ {
- err = hc.Sync(context.Background())
+ err = hc.Sync(t.Context())
require.NoErrorf(t, err, "#%d: unexpected error during Sync", i)
if g := hc.endpoints[hc.pinned]; g != pinnedEndpoint {
@@ -947,7 +947,7 @@ func TestHTTPClusterClientSyncUnpinEndpoint(t *testing.T) {
wants := []string{"http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002"}
for i := 0; i < 3; i++ {
- err = hc.Sync(context.Background())
+ err = hc.Sync(t.Context())
require.NoErrorf(t, err, "#%d: unexpected error during Sync", i)
if g := hc.endpoints[hc.pinned]; g.String() != wants[i] {
@@ -988,7 +988,7 @@ func TestHTTPClusterClientSyncPinLeaderEndpoint(t *testing.T) {
wants := []string{"http://127.0.0.1:4003", "http://127.0.0.1:4002"}
for i, want := range wants {
- err := hc.Sync(context.Background())
+ err := hc.Sync(t.Context())
require.NoErrorf(t, err, "#%d: unexpected error during Sync", i)
pinned := hc.endpoints[hc.pinned].String()
diff --git a/server/internal/clientv2/keys_test.go b/server/internal/clientv2/keys_test.go
index 1bc6927b68e8..7bc264a7b83f 100644
--- a/server/internal/clientv2/keys_test.go
+++ b/server/internal/clientv2/keys_test.go
@@ -15,7 +15,6 @@
package client
import (
- "context"
"errors"
"fmt"
"io"
@@ -875,7 +874,7 @@ func TestHTTPWatcherNextWaitAction(t *testing.T) {
nextWait: initAction,
}
- resp, err := watcher.Next(context.Background())
+ resp, err := watcher.Next(t.Context())
if err != nil {
t.Errorf("non-nil error: %#v", err)
}
@@ -925,7 +924,7 @@ func TestHTTPWatcherNextFail(t *testing.T) {
nextWait: act,
}
- resp, err := watcher.Next(context.Background())
+ resp, err := watcher.Next(t.Context())
if err == nil {
t.Errorf("#%d: expected non-nil error", i)
}
@@ -1073,7 +1072,7 @@ func TestHTTPKeysAPISetAction(t *testing.T) {
for i, tt := range tests {
client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
kAPI := httpKeysAPI{client: client}
- kAPI.Set(context.Background(), tt.key, tt.value, tt.opts)
+ kAPI.Set(t.Context(), tt.key, tt.value, tt.opts)
}
}
@@ -1102,7 +1101,7 @@ func TestHTTPKeysAPISetError(t *testing.T) {
for i, tt := range tests {
kAPI := httpKeysAPI{client: tt}
- resp, err := kAPI.Set(context.Background(), "/foo", "bar", nil)
+ resp, err := kAPI.Set(t.Context(), "/foo", "bar", nil)
if err == nil {
t.Errorf("#%d: received nil error", i)
}
@@ -1129,7 +1128,7 @@ func TestHTTPKeysAPISetResponse(t *testing.T) {
}
kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
- resp, err := kAPI.Set(context.Background(), "/foo/bar/baz", "snarf", nil)
+ resp, err := kAPI.Set(t.Context(), "/foo/bar/baz", "snarf", nil)
if err != nil {
t.Errorf("non-nil error: %#v", err)
}
@@ -1184,7 +1183,7 @@ func TestHTTPKeysAPIGetAction(t *testing.T) {
for i, tt := range tests {
client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
kAPI := httpKeysAPI{client: client}
- kAPI.Get(context.Background(), tt.key, tt.opts)
+ kAPI.Get(t.Context(), tt.key, tt.opts)
}
}
@@ -1213,7 +1212,7 @@ func TestHTTPKeysAPIGetError(t *testing.T) {
for i, tt := range tests {
kAPI := httpKeysAPI{client: tt}
- resp, err := kAPI.Get(context.Background(), "/foo", nil)
+ resp, err := kAPI.Get(t.Context(), "/foo", nil)
if err == nil {
t.Errorf("#%d: received nil error", i)
}
@@ -1246,7 +1245,7 @@ func TestHTTPKeysAPIGetResponse(t *testing.T) {
}
kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
- resp, err := kAPI.Get(context.Background(), "/foo/bar", &GetOptions{Recursive: true})
+ resp, err := kAPI.Get(t.Context(), "/foo/bar", &GetOptions{Recursive: true})
if err != nil {
t.Errorf("non-nil error: %#v", err)
}
@@ -1303,7 +1302,7 @@ func TestHTTPKeysAPIDeleteAction(t *testing.T) {
for i, tt := range tests {
client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
kAPI := httpKeysAPI{client: client}
- kAPI.Delete(context.Background(), tt.key, tt.opts)
+ kAPI.Delete(t.Context(), tt.key, tt.opts)
}
}
@@ -1332,7 +1331,7 @@ func TestHTTPKeysAPIDeleteError(t *testing.T) {
for i, tt := range tests {
kAPI := httpKeysAPI{client: tt}
- resp, err := kAPI.Delete(context.Background(), "/foo", nil)
+ resp, err := kAPI.Delete(t.Context(), "/foo", nil)
if err == nil {
t.Errorf("#%d: received nil error", i)
}
@@ -1359,7 +1358,7 @@ func TestHTTPKeysAPIDeleteResponse(t *testing.T) {
}
kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
- resp, err := kAPI.Delete(context.Background(), "/foo/bar/baz", nil)
+ resp, err := kAPI.Delete(t.Context(), "/foo/bar/baz", nil)
if err != nil {
t.Errorf("non-nil error: %#v", err)
}
@@ -1379,7 +1378,7 @@ func TestHTTPKeysAPICreateAction(t *testing.T) {
}
kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
- kAPI.Create(context.Background(), "/foo", "bar")
+ kAPI.Create(t.Context(), "/foo", "bar")
}
func TestHTTPKeysAPICreateInOrderAction(t *testing.T) {
@@ -1389,7 +1388,7 @@ func TestHTTPKeysAPICreateInOrderAction(t *testing.T) {
TTL: 0,
}
kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
- kAPI.CreateInOrder(context.Background(), "/foo", "bar", nil)
+ kAPI.CreateInOrder(t.Context(), "/foo", "bar", nil)
}
func TestHTTPKeysAPIUpdateAction(t *testing.T) {
@@ -1403,7 +1402,7 @@ func TestHTTPKeysAPIUpdateAction(t *testing.T) {
}
kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
- kAPI.Update(context.Background(), "/foo", "bar")
+ kAPI.Update(t.Context(), "/foo", "bar")
}
func TestNodeTTLDuration(t *testing.T) {
diff --git a/server/internal/clientv2/members_test.go b/server/internal/clientv2/members_test.go
index 4d3114a49097..e93b55c476f2 100644
--- a/server/internal/clientv2/members_test.go
+++ b/server/internal/clientv2/members_test.go
@@ -15,7 +15,6 @@
package client
import (
- "context"
"encoding/json"
"errors"
"net/http"
@@ -340,7 +339,7 @@ func TestHTTPMembersAPIAddSuccess(t *testing.T) {
PeerURLs: []string{"http://127.0.0.1:7002"},
}
- m, err := mAPI.Add(context.Background(), "http://127.0.0.1:7002")
+ m, err := mAPI.Add(t.Context(), "http://127.0.0.1:7002")
if err != nil {
t.Errorf("got non-nil err: %#v", err)
}
@@ -415,7 +414,7 @@ func TestHTTPMembersAPIAddError(t *testing.T) {
for i, tt := range tests {
mAPI := &httpMembersAPI{client: tt.client}
- m, err := mAPI.Add(context.Background(), tt.peerURL)
+ m, err := mAPI.Add(t.Context(), tt.peerURL)
if err == nil {
t.Errorf("#%d: got nil err", i)
}
@@ -443,7 +442,7 @@ func TestHTTPMembersAPIRemoveSuccess(t *testing.T) {
},
}
- if err := mAPI.Remove(context.Background(), "94088180e21eb87b"); err != nil {
+ if err := mAPI.Remove(t.Context(), "94088180e21eb87b"); err != nil {
t.Errorf("got non-nil err: %#v", err)
}
}
@@ -465,7 +464,7 @@ func TestHTTPMembersAPIRemoveFail(t *testing.T) {
for i, tt := range tests {
mAPI := &httpMembersAPI{client: tt}
- if err := mAPI.Remove(context.Background(), "94088180e21eb87b"); err == nil {
+ if err := mAPI.Remove(t.Context(), "94088180e21eb87b"); err == nil {
t.Errorf("#%d: got nil err", i)
}
}
@@ -493,7 +492,7 @@ func TestHTTPMembersAPIListSuccess(t *testing.T) {
},
}
- m, err := mAPI.List(context.Background())
+ m, err := mAPI.List(t.Context())
if err != nil {
t.Errorf("got non-nil err: %#v", err)
}
@@ -523,7 +522,7 @@ func TestHTTPMembersAPIListError(t *testing.T) {
for i, tt := range tests {
mAPI := &httpMembersAPI{client: tt}
- ms, err := mAPI.List(context.Background())
+ ms, err := mAPI.List(t.Context())
if err == nil {
t.Errorf("#%d: got nil err", i)
}
@@ -553,7 +552,7 @@ func TestHTTPMembersAPILeaderSuccess(t *testing.T) {
ClientURLs: []string{"http://127.0.0.1:4002"},
}
- m, err := mAPI.Leader(context.Background())
+ m, err := mAPI.Leader(t.Context())
if err != nil {
t.Errorf("err = %v, want %v", err, nil)
}
@@ -583,7 +582,7 @@ func TestHTTPMembersAPILeaderError(t *testing.T) {
for i, tt := range tests {
mAPI := &httpMembersAPI{client: tt}
- m, err := mAPI.Leader(context.Background())
+ m, err := mAPI.Leader(t.Context())
if err == nil {
t.Errorf("#%d: err = nil, want not nil", i)
}
diff --git a/server/lease/leasehttp/http.go b/server/lease/leasehttp/http.go
index d5572c3aafc7..8f00b652e755 100644
--- a/server/lease/leasehttp/http.go
+++ b/server/lease/leasehttp/http.go
@@ -23,6 +23,8 @@ import (
"net/http"
"time"
+ "google.golang.org/protobuf/proto"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/pkg/v3/httputil"
"go.etcd.io/etcd/server/v3/lease"
@@ -63,7 +65,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case LeasePrefix:
lreq := pb.LeaseKeepAliveRequest{}
- if uerr := lreq.Unmarshal(b); uerr != nil {
+ if uerr := proto.Unmarshal(b, &lreq); uerr != nil {
http.Error(w, "error unmarshalling request", http.StatusBadRequest)
return
}
@@ -73,6 +75,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, ErrLeaseHTTPTimeout.Error(), http.StatusRequestTimeout)
return
}
+ // gofail: var beforeServeHTTPLeaseRenew struct{}
ttl, rerr := h.l.Renew(lease.LeaseID(lreq.ID))
if rerr != nil {
if errors.Is(rerr, lease.ErrLeaseNotFound) {
@@ -85,7 +88,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// TODO: fill out ResponseHeader
resp := &pb.LeaseKeepAliveResponse{ID: lreq.ID, TTL: ttl}
- v, err = resp.Marshal()
+ v, err = proto.Marshal(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -93,7 +96,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case LeaseInternalPrefix:
lreq := leasepb.LeaseInternalRequest{}
- if lerr := lreq.Unmarshal(b); lerr != nil {
+ if lerr := proto.Unmarshal(b, &lreq); lerr != nil {
http.Error(w, "error unmarshalling request", http.StatusBadRequest)
return
}
@@ -137,7 +140,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
- v, err = resp.Marshal()
+ v, err = proto.Marshal(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -156,7 +159,7 @@ func (h *leaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// TODO: Batch request in future?
func RenewHTTP(ctx context.Context, id lease.LeaseID, url string, rt http.RoundTripper) (int64, error) {
// will post lreq protobuf to leader
- lreq, err := (&pb.LeaseKeepAliveRequest{ID: int64(id)}).Marshal()
+ lreq, err := proto.Marshal(&pb.LeaseKeepAliveRequest{ID: int64(id)})
if err != nil {
return -1, err
}
@@ -171,8 +174,8 @@ func RenewHTTP(ctx context.Context, id lease.LeaseID, url string, rt http.RoundT
if err != nil {
return -1, err
}
+ req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/protobuf")
- req.Cancel = ctx.Done()
resp, err := cc.Do(req)
if err != nil {
@@ -196,7 +199,7 @@ func RenewHTTP(ctx context.Context, id lease.LeaseID, url string, rt http.RoundT
}
lresp := &pb.LeaseKeepAliveResponse{}
- if err := lresp.Unmarshal(b); err != nil {
+ if err := proto.Unmarshal(b, lresp); err != nil {
return -1, fmt.Errorf(`lease: %w. data = "%s"`, err, b)
}
if lresp.ID != int64(id) {
@@ -208,12 +211,12 @@ func RenewHTTP(ctx context.Context, id lease.LeaseID, url string, rt http.RoundT
// TimeToLiveHTTP retrieves lease information of the given lease ID.
func TimeToLiveHTTP(ctx context.Context, id lease.LeaseID, keys bool, url string, rt http.RoundTripper) (*leasepb.LeaseInternalResponse, error) {
// will post lreq protobuf to leader
- lreq, err := (&leasepb.LeaseInternalRequest{
+ lreq, err := proto.Marshal(&leasepb.LeaseInternalRequest{
LeaseTimeToLiveRequest: &pb.LeaseTimeToLiveRequest{
ID: int64(id),
Keys: keys,
},
- }).Marshal()
+ })
if err != nil {
return nil, err
}
@@ -253,7 +256,7 @@ func TimeToLiveHTTP(ctx context.Context, id lease.LeaseID, keys bool, url string
}
lresp := &leasepb.LeaseInternalResponse{}
- if err := lresp.Unmarshal(b); err != nil {
+ if err := proto.Unmarshal(b, lresp); err != nil {
return nil, fmt.Errorf(`lease: %w. data = "%s"`, err, string(b))
}
if lresp.LeaseTimeToLiveResponse.ID != int64(id) {
diff --git a/server/lease/leasehttp/http_test.go b/server/lease/leasehttp/http_test.go
index 7fb284ff41f9..f606486b5bae 100644
--- a/server/lease/leasehttp/http_test.go
+++ b/server/lease/leasehttp/http_test.go
@@ -15,7 +15,6 @@
package leasehttp
import (
- "context"
"net/http"
"net/http/httptest"
"testing"
@@ -42,7 +41,7 @@ func TestRenewHTTP(t *testing.T) {
ts := httptest.NewServer(NewHandler(le, waitReady))
defer ts.Close()
- ttl, err := RenewHTTP(context.TODO(), l.ID, ts.URL+LeasePrefix, http.DefaultTransport)
+ ttl, err := RenewHTTP(t.Context(), l.ID, ts.URL+LeasePrefix, http.DefaultTransport)
if err != nil {
t.Fatal(err)
}
@@ -66,7 +65,7 @@ func TestTimeToLiveHTTP(t *testing.T) {
ts := httptest.NewServer(NewHandler(le, waitReady))
defer ts.Close()
- resp, err := TimeToLiveHTTP(context.TODO(), l.ID, true, ts.URL+LeaseInternalPrefix, http.DefaultTransport)
+ resp, err := TimeToLiveHTTP(t.Context(), l.ID, true, ts.URL+LeaseInternalPrefix, http.DefaultTransport)
if err != nil {
t.Fatal(err)
}
@@ -80,14 +79,14 @@ func TestTimeToLiveHTTP(t *testing.T) {
func TestRenewHTTPTimeout(t *testing.T) {
testApplyTimeout(t, func(l *lease.Lease, serverURL string) error {
- _, err := RenewHTTP(context.TODO(), l.ID, serverURL+LeasePrefix, http.DefaultTransport)
+ _, err := RenewHTTP(t.Context(), l.ID, serverURL+LeasePrefix, http.DefaultTransport)
return err
})
}
func TestTimeToLiveHTTPTimeout(t *testing.T) {
testApplyTimeout(t, func(l *lease.Lease, serverURL string) error {
- _, err := TimeToLiveHTTP(context.TODO(), l.ID, true, serverURL+LeaseInternalPrefix, http.DefaultTransport)
+ _, err := TimeToLiveHTTP(t.Context(), l.ID, true, serverURL+LeaseInternalPrefix, http.DefaultTransport)
return err
})
}
diff --git a/server/lease/leasepb/lease.pb.go b/server/lease/leasepb/lease.pb.go
index ee833d31a730..7d8ea1b48f13 100644
--- a/server/lease/leasepb/lease.pb.go
+++ b/server/lease/leasepb/lease.pb.go
@@ -1,735 +1,240 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: lease.proto
package leasepb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
etcdserverpb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Lease struct {
- ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
- TTL int64 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"`
- RemainingTTL int64 `protobuf:"varint,3,opt,name=RemainingTTL,proto3" json:"RemainingTTL,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+ TTL int64 `protobuf:"varint,2,opt,name=TTL,proto3" json:"TTL,omitempty"`
+ RemainingTTL int64 `protobuf:"varint,3,opt,name=RemainingTTL,proto3" json:"RemainingTTL,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Lease) Reset() { *m = Lease{} }
-func (m *Lease) String() string { return proto.CompactTextString(m) }
-func (*Lease) ProtoMessage() {}
-func (*Lease) Descriptor() ([]byte, []int) {
- return fileDescriptor_3dd57e402472b33a, []int{0}
-}
-func (m *Lease) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Lease) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Lease.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Lease) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Lease.Merge(m, src)
-}
-func (m *Lease) XXX_Size() int {
- return m.Size()
-}
-func (m *Lease) XXX_DiscardUnknown() {
- xxx_messageInfo_Lease.DiscardUnknown(m)
+func (x *Lease) Reset() {
+ *x = Lease{}
+ mi := &file_lease_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-var xxx_messageInfo_Lease proto.InternalMessageInfo
-
-type LeaseInternalRequest struct {
- LeaseTimeToLiveRequest *etcdserverpb.LeaseTimeToLiveRequest `protobuf:"bytes,1,opt,name=LeaseTimeToLiveRequest,proto3" json:"LeaseTimeToLiveRequest,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *Lease) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseInternalRequest) Reset() { *m = LeaseInternalRequest{} }
-func (m *LeaseInternalRequest) String() string { return proto.CompactTextString(m) }
-func (*LeaseInternalRequest) ProtoMessage() {}
-func (*LeaseInternalRequest) Descriptor() ([]byte, []int) {
- return fileDescriptor_3dd57e402472b33a, []int{1}
-}
-func (m *LeaseInternalRequest) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseInternalRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseInternalRequest.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *LeaseInternalRequest) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseInternalRequest.Merge(m, src)
-}
-func (m *LeaseInternalRequest) XXX_Size() int {
- return m.Size()
-}
-func (m *LeaseInternalRequest) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseInternalRequest.DiscardUnknown(m)
-}
+func (*Lease) ProtoMessage() {}
-var xxx_messageInfo_LeaseInternalRequest proto.InternalMessageInfo
-
-type LeaseInternalResponse struct {
- LeaseTimeToLiveResponse *etcdserverpb.LeaseTimeToLiveResponse `protobuf:"bytes,1,opt,name=LeaseTimeToLiveResponse,proto3" json:"LeaseTimeToLiveResponse,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
-}
-
-func (m *LeaseInternalResponse) Reset() { *m = LeaseInternalResponse{} }
-func (m *LeaseInternalResponse) String() string { return proto.CompactTextString(m) }
-func (*LeaseInternalResponse) ProtoMessage() {}
-func (*LeaseInternalResponse) Descriptor() ([]byte, []int) {
- return fileDescriptor_3dd57e402472b33a, []int{2}
-}
-func (m *LeaseInternalResponse) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *LeaseInternalResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_LeaseInternalResponse.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (x *Lease) ProtoReflect() protoreflect.Message {
+ mi := &file_lease_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *LeaseInternalResponse) XXX_Merge(src proto.Message) {
- xxx_messageInfo_LeaseInternalResponse.Merge(m, src)
-}
-func (m *LeaseInternalResponse) XXX_Size() int {
- return m.Size()
-}
-func (m *LeaseInternalResponse) XXX_DiscardUnknown() {
- xxx_messageInfo_LeaseInternalResponse.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_LeaseInternalResponse proto.InternalMessageInfo
-func init() {
- proto.RegisterType((*Lease)(nil), "leasepb.Lease")
- proto.RegisterType((*LeaseInternalRequest)(nil), "leasepb.LeaseInternalRequest")
- proto.RegisterType((*LeaseInternalResponse)(nil), "leasepb.LeaseInternalResponse")
-}
-
-func init() { proto.RegisterFile("lease.proto", fileDescriptor_3dd57e402472b33a) }
-
-var fileDescriptor_3dd57e402472b33a = []byte{
- // 283 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x49, 0x4d, 0x2c,
- 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07, 0x73, 0x0a, 0x92, 0xa4, 0x44, 0xd2,
- 0xf3, 0xd3, 0xf3, 0xc1, 0x62, 0xfa, 0x20, 0x16, 0x44, 0x5a, 0x4a, 0x3e, 0xb5, 0x24, 0x39, 0x45,
- 0x3f, 0xb1, 0x20, 0x53, 0x1f, 0xc4, 0x28, 0x4e, 0x2d, 0x2a, 0x4b, 0x2d, 0x2a, 0x48, 0xd2, 0x2f,
- 0x2a, 0x48, 0x86, 0x28, 0x50, 0xf2, 0xe5, 0x62, 0xf5, 0x01, 0x99, 0x20, 0xc4, 0xc7, 0xc5, 0xe4,
- 0xe9, 0x22, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x1c, 0xc4, 0xe4, 0xe9, 0x22, 0x24, 0xc0, 0xc5, 0x1c,
- 0x12, 0xe2, 0x23, 0xc1, 0x04, 0x16, 0x00, 0x31, 0x85, 0x94, 0xb8, 0x78, 0x82, 0x52, 0x73, 0x13,
- 0x33, 0xf3, 0x32, 0xf3, 0xd2, 0x41, 0x52, 0xcc, 0x60, 0x29, 0x14, 0x31, 0xa5, 0x12, 0x2e, 0x11,
- 0xb0, 0x71, 0x9e, 0x79, 0x25, 0xa9, 0x45, 0x79, 0x89, 0x39, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5,
- 0x25, 0x42, 0x31, 0x5c, 0x62, 0x60, 0xf1, 0x90, 0xcc, 0xdc, 0xd4, 0x90, 0x7c, 0x9f, 0xcc, 0xb2,
- 0x54, 0xa8, 0x0c, 0xd8, 0x46, 0x6e, 0x23, 0x15, 0x3d, 0x64, 0xf7, 0xe9, 0x61, 0x57, 0x1b, 0x84,
- 0xc3, 0x0c, 0xa5, 0x0a, 0x2e, 0x51, 0x34, 0x5b, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xe2,
- 0xb9, 0xc4, 0x31, 0xb4, 0x40, 0xa4, 0xa0, 0xf6, 0xaa, 0x12, 0xb0, 0x17, 0xa2, 0x38, 0x08, 0x97,
- 0x29, 0x4e, 0x9e, 0x27, 0x1e, 0xca, 0x31, 0x5c, 0x78, 0x28, 0xc7, 0x70, 0xe2, 0x91, 0x1c, 0xe3,
- 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xce, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x9f, 0x9e,
- 0x0f, 0x36, 0x5b, 0x2f, 0x33, 0x1f, 0x1c, 0xf6, 0xfa, 0x10, 0x4b, 0xf4, 0xcb, 0x8c, 0xf5, 0xc1,
- 0x51, 0xa6, 0x0f, 0x8d, 0x38, 0x6b, 0x28, 0x9d, 0xc4, 0x06, 0x8e, 0x10, 0x63, 0x40, 0x00, 0x00,
- 0x00, 0xff, 0xff, 0x0e, 0x16, 0x3b, 0xc4, 0xdf, 0x01, 0x00, 0x00,
+// Deprecated: Use Lease.ProtoReflect.Descriptor instead.
+func (*Lease) Descriptor() ([]byte, []int) {
+ return file_lease_proto_rawDescGZIP(), []int{0}
}
-func (m *Lease) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Lease) GetID() int64 {
+ if x != nil {
+ return x.ID
}
- return dAtA[:n], nil
+ return 0
}
-func (m *Lease) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
-
-func (m *Lease) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.RemainingTTL != 0 {
- i = encodeVarintLease(dAtA, i, uint64(m.RemainingTTL))
- i--
- dAtA[i] = 0x18
+func (x *Lease) GetTTL() int64 {
+ if x != nil {
+ return x.TTL
}
- if m.TTL != 0 {
- i = encodeVarintLease(dAtA, i, uint64(m.TTL))
- i--
- dAtA[i] = 0x10
- }
- if m.ID != 0 {
- i = encodeVarintLease(dAtA, i, uint64(m.ID))
- i--
- dAtA[i] = 0x8
- }
- return len(dAtA) - i, nil
+ return 0
}
-func (m *LeaseInternalRequest) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Lease) GetRemainingTTL() int64 {
+ if x != nil {
+ return x.RemainingTTL
}
- return dAtA[:n], nil
+ return 0
}
-func (m *LeaseInternalRequest) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+type LeaseInternalRequest struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ LeaseTimeToLiveRequest *etcdserverpb.LeaseTimeToLiveRequest `protobuf:"bytes,1,opt,name=LeaseTimeToLiveRequest,proto3" json:"LeaseTimeToLiveRequest,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *LeaseInternalRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.LeaseTimeToLiveRequest != nil {
- {
- size, err := m.LeaseTimeToLiveRequest.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintLease(dAtA, i, uint64(size))
- }
- i--
- dAtA[i] = 0xa
- }
- return len(dAtA) - i, nil
+func (x *LeaseInternalRequest) Reset() {
+ *x = LeaseInternalRequest{}
+ mi := &file_lease_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *LeaseInternalResponse) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *LeaseInternalRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseInternalResponse) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
-}
+func (*LeaseInternalRequest) ProtoMessage() {}
-func (m *LeaseInternalResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.LeaseTimeToLiveResponse != nil {
- {
- size, err := m.LeaseTimeToLiveResponse.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintLease(dAtA, i, uint64(size))
+func (x *LeaseInternalRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_lease_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0xa
+ return ms
}
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func encodeVarintLease(dAtA []byte, offset int, v uint64) int {
- offset -= sovLease(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *Lease) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.ID != 0 {
- n += 1 + sovLease(uint64(m.ID))
- }
- if m.TTL != 0 {
- n += 1 + sovLease(uint64(m.TTL))
- }
- if m.RemainingTTL != 0 {
- n += 1 + sovLease(uint64(m.RemainingTTL))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use LeaseInternalRequest.ProtoReflect.Descriptor instead.
+func (*LeaseInternalRequest) Descriptor() ([]byte, []int) {
+ return file_lease_proto_rawDescGZIP(), []int{1}
}
-func (m *LeaseInternalRequest) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.LeaseTimeToLiveRequest != nil {
- l = m.LeaseTimeToLiveRequest.Size()
- n += 1 + l + sovLease(uint64(l))
+func (x *LeaseInternalRequest) GetLeaseTimeToLiveRequest() *etcdserverpb.LeaseTimeToLiveRequest {
+ if x != nil {
+ return x.LeaseTimeToLiveRequest
}
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+ return nil
}
-func (m *LeaseInternalResponse) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- if m.LeaseTimeToLiveResponse != nil {
- l = m.LeaseTimeToLiveResponse.Size()
- n += 1 + l + sovLease(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+type LeaseInternalResponse struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ LeaseTimeToLiveResponse *etcdserverpb.LeaseTimeToLiveResponse `protobuf:"bytes,1,opt,name=LeaseTimeToLiveResponse,proto3" json:"LeaseTimeToLiveResponse,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func sovLease(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozLease(x uint64) (n int) {
- return sovLease(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+func (x *LeaseInternalResponse) Reset() {
+ *x = LeaseInternalResponse{}
+ mi := &file_lease_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Lease) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Lease: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Lease: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
- }
- m.ID = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.ID |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field TTL", wireType)
- }
- m.TTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.TTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field RemainingTTL", wireType)
- }
- m.RemainingTTL = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.RemainingTTL |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- default:
- iNdEx = preIndex
- skippy, err := skipLease(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthLease
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+func (x *LeaseInternalResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *LeaseInternalRequest) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseInternalRequest: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseInternalRequest: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field LeaseTimeToLiveRequest", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthLease
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthLease
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.LeaseTimeToLiveRequest == nil {
- m.LeaseTimeToLiveRequest = &etcdserverpb.LeaseTimeToLiveRequest{}
- }
- if err := m.LeaseTimeToLiveRequest.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipLease(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthLease
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
+
+func (*LeaseInternalResponse) ProtoMessage() {}
+
+func (x *LeaseInternalResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_lease_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
+ return ms
}
+ return mi.MessageOf(x)
+}
- if iNdEx > l {
- return io.ErrUnexpectedEOF
- }
- return nil
+// Deprecated: Use LeaseInternalResponse.ProtoReflect.Descriptor instead.
+func (*LeaseInternalResponse) Descriptor() ([]byte, []int) {
+ return file_lease_proto_rawDescGZIP(), []int{2}
}
-func (m *LeaseInternalResponse) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: LeaseInternalResponse: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: LeaseInternalResponse: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field LeaseTimeToLiveResponse", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowLease
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthLease
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthLease
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.LeaseTimeToLiveResponse == nil {
- m.LeaseTimeToLiveResponse = &etcdserverpb.LeaseTimeToLiveResponse{}
- }
- if err := m.LeaseTimeToLiveResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipLease(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthLease
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *LeaseInternalResponse) GetLeaseTimeToLiveResponse() *etcdserverpb.LeaseTimeToLiveResponse {
+ if x != nil {
+ return x.LeaseTimeToLiveResponse
}
return nil
}
-func skipLease(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowLease
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowLease
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowLease
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthLease
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupLease
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthLease
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+
+var File_lease_proto protoreflect.FileDescriptor
+
+const file_lease_proto_rawDesc = "" +
+ "\n" +
+ "\vlease.proto\x12\aleasepb\x1a\x1fetcd/api/etcdserverpb/rpc.proto\"M\n" +
+ "\x05Lease\x12\x0e\n" +
+ "\x02ID\x18\x01 \x01(\x03R\x02ID\x12\x10\n" +
+ "\x03TTL\x18\x02 \x01(\x03R\x03TTL\x12\"\n" +
+ "\fRemainingTTL\x18\x03 \x01(\x03R\fRemainingTTL\"t\n" +
+ "\x14LeaseInternalRequest\x12\\\n" +
+ "\x16LeaseTimeToLiveRequest\x18\x01 \x01(\v2$.etcdserverpb.LeaseTimeToLiveRequestR\x16LeaseTimeToLiveRequest\"x\n" +
+ "\x15LeaseInternalResponse\x12_\n" +
+ "\x17LeaseTimeToLiveResponse\x18\x01 \x01(\v2%.etcdserverpb.LeaseTimeToLiveResponseR\x17LeaseTimeToLiveResponseB1Z/go.etcd.io/etcd/server/v3/lease/leasepb;leasepbb\x06proto3"
var (
- ErrInvalidLengthLease = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowLease = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupLease = fmt.Errorf("proto: unexpected end of group")
+ file_lease_proto_rawDescOnce sync.Once
+ file_lease_proto_rawDescData []byte
)
+
+func file_lease_proto_rawDescGZIP() []byte {
+ file_lease_proto_rawDescOnce.Do(func() {
+ file_lease_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_lease_proto_rawDesc), len(file_lease_proto_rawDesc)))
+ })
+ return file_lease_proto_rawDescData
+}
+
+var file_lease_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_lease_proto_goTypes = []any{
+ (*Lease)(nil), // 0: leasepb.Lease
+ (*LeaseInternalRequest)(nil), // 1: leasepb.LeaseInternalRequest
+ (*LeaseInternalResponse)(nil), // 2: leasepb.LeaseInternalResponse
+ (*etcdserverpb.LeaseTimeToLiveRequest)(nil), // 3: etcdserverpb.LeaseTimeToLiveRequest
+ (*etcdserverpb.LeaseTimeToLiveResponse)(nil), // 4: etcdserverpb.LeaseTimeToLiveResponse
+}
+var file_lease_proto_depIdxs = []int32{
+ 3, // 0: leasepb.LeaseInternalRequest.LeaseTimeToLiveRequest:type_name -> etcdserverpb.LeaseTimeToLiveRequest
+ 4, // 1: leasepb.LeaseInternalResponse.LeaseTimeToLiveResponse:type_name -> etcdserverpb.LeaseTimeToLiveResponse
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_lease_proto_init() }
+func file_lease_proto_init() {
+ if File_lease_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_lease_proto_rawDesc), len(file_lease_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 3,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_lease_proto_goTypes,
+ DependencyIndexes: file_lease_proto_depIdxs,
+ MessageInfos: file_lease_proto_msgTypes,
+ }.Build()
+ File_lease_proto = out.File
+ file_lease_proto_goTypes = nil
+ file_lease_proto_depIdxs = nil
+}
diff --git a/server/lease/leasepb/lease.proto b/server/lease/leasepb/lease.proto
index d63179754835..2ffcfcc8a046 100644
--- a/server/lease/leasepb/lease.proto
+++ b/server/lease/leasepb/lease.proto
@@ -1,17 +1,10 @@
syntax = "proto3";
package leasepb;
-import "gogoproto/gogo.proto";
import "etcd/api/etcdserverpb/rpc.proto";
option go_package = "go.etcd.io/etcd/server/v3/lease/leasepb;leasepb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-option (gogoproto.goproto_enum_prefix_all) = false;
-
message Lease {
int64 ID = 1;
int64 TTL = 2;
diff --git a/server/lease/lessor_bench_test.go b/server/lease/lessor_bench_test.go
index 8e6ff791b193..b28ba45c8e87 100644
--- a/server/lease/lessor_bench_test.go
+++ b/server/lease/lessor_bench_test.go
@@ -60,9 +60,9 @@ func demote(le *lessor) {
}
// return new lessor and tearDown to release resource
-func setUp(t testing.TB) (le *lessor, tearDown func()) {
+func setUp(tb testing.TB) (le *lessor, tearDown func()) {
lg := zap.NewNop()
- be, _ := betesting.NewDefaultTmpBackend(t)
+ be, _ := betesting.NewDefaultTmpBackend(tb)
// MinLeaseTTL is negative, so we can grant expired lease in benchmark.
// ExpiredLeasesRetryInterval should small, so benchmark of findExpired will recheck expired lease.
le = newLessor(lg, be, nil, LessorConfig{MinLeaseTTL: -1000, ExpiredLeasesRetryInterval: 10 * time.Microsecond})
diff --git a/server/lease/lessor_test.go b/server/lease/lessor_test.go
index f46e2b399495..5e1b546e6f6e 100644
--- a/server/lease/lessor_test.go
+++ b/server/lease/lessor_test.go
@@ -101,7 +101,7 @@ func TestLessorGrant(t *testing.T) {
defer tx.Unlock()
lpb := schema.MustUnsafeGetLease(tx, int64(l.ID))
if lpb == nil {
- t.Errorf("lpb = %d, want not nil", lpb)
+ t.Errorf("lpb = nil, want not nil")
}
}
@@ -205,7 +205,7 @@ func TestLessorRevoke(t *testing.T) {
defer tx.Unlock()
lpb := schema.MustUnsafeGetLease(tx, int64(l.ID))
if lpb != nil {
- t.Errorf("lpb = %d, want nil", lpb)
+ t.Errorf("lpb = %s, want nil", lpb.String())
}
}
diff --git a/server/mock/mockstorage/storage_recorder.go b/server/mock/mockstorage/storage_recorder.go
index 41d2952e8a12..88e194a8798d 100644
--- a/server/mock/mockstorage/storage_recorder.go
+++ b/server/mock/mockstorage/storage_recorder.go
@@ -22,42 +22,42 @@ import (
"go.etcd.io/raft/v3/raftpb"
)
-type storageRecorder struct {
+type StorageRecorder struct {
testutil.Recorder
dbPath string // must have '/' suffix if set
}
-func NewStorageRecorder(db string) *storageRecorder {
- return &storageRecorder{&testutil.RecorderBuffered{}, db}
+func NewStorageRecorder(db string) *StorageRecorder {
+ return &StorageRecorder{&testutil.RecorderBuffered{}, db}
}
-func NewStorageRecorderStream(db string) *storageRecorder {
- return &storageRecorder{testutil.NewRecorderStream(), db}
+func NewStorageRecorderStream(db string) *StorageRecorder {
+ return &StorageRecorder{testutil.NewRecorderStream(), db}
}
-func (p *storageRecorder) Save(st raftpb.HardState, ents []raftpb.Entry) error {
+func (p *StorageRecorder) Save(st *raftpb.HardState, ents []*raftpb.Entry) error {
p.Record(testutil.Action{Name: "Save"})
return nil
}
-func (p *storageRecorder) SaveSnap(st raftpb.Snapshot) error {
+func (p *StorageRecorder) SaveSnap(st *raftpb.Snapshot) error {
if !raft.IsEmptySnap(st) {
p.Record(testutil.Action{Name: "SaveSnap"})
}
return nil
}
-func (p *storageRecorder) Release(st raftpb.Snapshot) error {
+func (p *StorageRecorder) Release(st *raftpb.Snapshot) error {
if !raft.IsEmptySnap(st) {
p.Record(testutil.Action{Name: "Release"})
}
return nil
}
-func (p *storageRecorder) Sync() error {
+func (p *StorageRecorder) Sync() error {
p.Record(testutil.Action{Name: "Sync"})
return nil
}
-func (p *storageRecorder) Close() error { return nil }
-func (p *storageRecorder) MinimalEtcdVersion() *semver.Version { return nil }
+func (p *StorageRecorder) Close() error { return nil }
+func (p *StorageRecorder) MinimalEtcdVersion() *semver.Version { return nil }
diff --git a/server/proxy/grpcproxy/adapter/chan_stream.go b/server/proxy/grpcproxy/adapter/chan_stream.go
index 802c841d3d23..89d57ac8ac33 100644
--- a/server/proxy/grpcproxy/adapter/chan_stream.go
+++ b/server/proxy/grpcproxy/adapter/chan_stream.go
@@ -16,6 +16,7 @@ package adapter
import (
"context"
+ "io"
"maps"
"google.golang.org/grpc"
@@ -46,9 +47,9 @@ func (ss *chanServerStream) SendHeader(md metadata.MD) error {
ss.headerc = nil
ss.headers = nil
return nil
- case <-ss.Context().Done():
+ case <-ss.Context().Done(): //nolint:staticcheck // TODO: remove for a supported version
}
- return ss.Context().Err()
+ return ss.Context().Err() //nolint:staticcheck // TODO: remove for a supported version
}
func (ss *chanServerStream) SetHeader(md metadata.MD) error {
@@ -139,7 +140,8 @@ func (s *chanStream) RecvMsg(m any) error {
}
func newPipeStream(ctx context.Context, ssHandler func(chanServerStream) error) chanClientStream {
- // ch1 is buffered so server can send error on close
+ // ch1 is buffered so the server can deliver a terminal status
+ // (real error or io.EOF) after the handler returns.
ch1, ch2 := make(chan any, 1), make(chan any)
headerc, trailerc := make(chan metadata.MD, 1), make(chan metadata.MD, 1)
@@ -152,12 +154,17 @@ func newPipeStream(ctx context.Context, ssHandler func(chanServerStream) error)
ss := chanServerStream{headerc, trailerc, srv, nil}
go func() {
- if err := ssHandler(ss); err != nil {
- select {
- case srv.sendc <- err:
- case <-sctx.Done():
- case <-cctx.Done():
- }
+ err := ssHandler(ss)
+ if err == nil {
+ // nil means the handler completed successfully;
+ // the gRPC ClientStream contract requires io.EOF:
+ // https://github.com/grpc/grpc-go/blob/v1.80.0/stream.go#L139-L147
+ err = io.EOF
+ }
+ select {
+ case srv.sendc <- err:
+ case <-sctx.Done():
+ case <-cctx.Done():
}
scancel()
ccancel()
diff --git a/server/proxy/grpcproxy/adapter/chan_stream_test.go b/server/proxy/grpcproxy/adapter/chan_stream_test.go
new file mode 100644
index 000000000000..ba9759774ec5
--- /dev/null
+++ b/server/proxy/grpcproxy/adapter/chan_stream_test.go
@@ -0,0 +1,83 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 adapter
+
+import (
+ "context"
+ "errors"
+ "io"
+ "testing"
+)
+
+// TestPipeStream_HandlerSuccessReturnsEOF verifies that when a server-streaming
+// handler completes successfully (returns nil), the client receives io.EOF from
+// RecvMsg. This matches the gRPC ClientStream contract:
+//
+// "It returns io.EOF when the stream completes successfully."
+// https://github.com/grpc/grpc-go/blob/v1.80.0/stream.go#L139-L147
+func TestPipeStream_HandlerSuccessReturnsEOF(t *testing.T) {
+ cs := newPipeStream(context.Background(), func(ss chanServerStream) error {
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ ss.SendMsg("msg1") //nolint:staticcheck
+ ss.SendMsg("msg2") //nolint:staticcheck
+ }()
+ <-done
+ return nil
+ })
+
+ for _, want := range []string{"msg1", "msg2"} {
+ var v any
+ if err := cs.RecvMsg(&v); err != nil { //nolint:staticcheck
+ t.Fatalf("RecvMsg() = %v, want nil", err)
+ }
+ if v != want {
+ t.Fatalf("RecvMsg() got %v, want %s", v, want)
+ }
+ }
+
+ var v any
+ if err := cs.RecvMsg(&v); !errors.Is(err, io.EOF) { //nolint:staticcheck
+ t.Fatalf("RecvMsg() = %v, want io.EOF (per gRPC ClientStream contract)", err)
+ }
+}
+
+// TestPipeStream_HandlerErrorPropagated verifies that when a handler returns a
+// non-nil error, the client receives that exact error from RecvMsg.
+func TestPipeStream_HandlerErrorPropagated(t *testing.T) {
+ handlerErr := errors.New("handler failed")
+ cs := newPipeStream(context.Background(), func(ss chanServerStream) error {
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ ss.SendMsg("partial") //nolint:staticcheck
+ }()
+ <-done
+ return handlerErr
+ })
+
+ var v any
+ if err := cs.RecvMsg(&v); err != nil { //nolint:staticcheck
+ t.Fatalf("RecvMsg() = %v, want nil", err)
+ }
+ if v != "partial" {
+ t.Fatalf("RecvMsg() got %v, want partial", v)
+ }
+
+ if err := cs.RecvMsg(&v); !errors.Is(err, handlerErr) { //nolint:staticcheck
+ t.Fatalf("RecvMsg() = %v, want %v", err, handlerErr)
+ }
+}
diff --git a/server/proxy/grpcproxy/adapter/election_client_adapter.go b/server/proxy/grpcproxy/adapter/election_client_adapter.go
index c7edaf70a473..b22034a9ffa0 100644
--- a/server/proxy/grpcproxy/adapter/election_client_adapter.go
+++ b/server/proxy/grpcproxy/adapter/election_client_adapter.go
@@ -58,24 +58,24 @@ type es2ecClientStream struct{ chanClientStream }
type es2ecServerStream struct{ chanServerStream }
func (s *es2ecClientStream) Send(rr *v3electionpb.LeaderRequest) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *es2ecClientStream) Recv() (*v3electionpb.LeaderResponse, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*v3electionpb.LeaderResponse), nil
}
func (s *es2ecServerStream) Send(rr *v3electionpb.LeaderResponse) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *es2ecServerStream) Recv() (*v3electionpb.LeaderRequest, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*v3electionpb.LeaderRequest), nil
diff --git a/server/proxy/grpcproxy/adapter/kv_client_adapter.go b/server/proxy/grpcproxy/adapter/kv_client_adapter.go
index 69e3a113c4fe..a8149b17dac1 100644
--- a/server/proxy/grpcproxy/adapter/kv_client_adapter.go
+++ b/server/proxy/grpcproxy/adapter/kv_client_adapter.go
@@ -18,6 +18,8 @@ import (
"context"
grpc "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
)
@@ -47,3 +49,7 @@ func (s *kvs2kvc) Txn(ctx context.Context, in *pb.TxnRequest, opts ...grpc.CallO
func (s *kvs2kvc) Compact(ctx context.Context, in *pb.CompactionRequest, opts ...grpc.CallOption) (*pb.CompactionResponse, error) {
return s.kvs.Compact(ctx, in)
}
+
+func (s *kvs2kvc) RangeStream(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (pb.KV_RangeStreamClient, error) {
+ return nil, status.Error(codes.Unimplemented, "RangeStream is not supported by the KV client adapter")
+}
diff --git a/server/proxy/grpcproxy/adapter/lease_client_adapter.go b/server/proxy/grpcproxy/adapter/lease_client_adapter.go
index bf76a5563e51..3c5423f5377e 100644
--- a/server/proxy/grpcproxy/adapter/lease_client_adapter.go
+++ b/server/proxy/grpcproxy/adapter/lease_client_adapter.go
@@ -60,24 +60,24 @@ type ls2lcClientStream struct{ chanClientStream }
type ls2lcServerStream struct{ chanServerStream }
func (s *ls2lcClientStream) Send(rr *pb.LeaseKeepAliveRequest) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ls2lcClientStream) Recv() (*pb.LeaseKeepAliveResponse, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.LeaseKeepAliveResponse), nil
}
func (s *ls2lcServerStream) Send(rr *pb.LeaseKeepAliveResponse) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ls2lcServerStream) Recv() (*pb.LeaseKeepAliveRequest, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.LeaseKeepAliveRequest), nil
diff --git a/server/proxy/grpcproxy/adapter/maintenance_client_adapter.go b/server/proxy/grpcproxy/adapter/maintenance_client_adapter.go
index 7b204451681b..dac5ae257bec 100644
--- a/server/proxy/grpcproxy/adapter/maintenance_client_adapter.go
+++ b/server/proxy/grpcproxy/adapter/maintenance_client_adapter.go
@@ -70,24 +70,24 @@ type ss2scClientStream struct{ chanClientStream }
type ss2scServerStream struct{ chanServerStream }
func (s *ss2scClientStream) Send(rr *pb.SnapshotRequest) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ss2scClientStream) Recv() (*pb.SnapshotResponse, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.SnapshotResponse), nil
}
func (s *ss2scServerStream) Send(rr *pb.SnapshotResponse) error {
- return s.SendMsg(rr)
+ return s.SendMsg(rr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ss2scServerStream) Recv() (*pb.SnapshotRequest, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.SnapshotRequest), nil
diff --git a/server/proxy/grpcproxy/adapter/watch_client_adapter.go b/server/proxy/grpcproxy/adapter/watch_client_adapter.go
index 828ed1b7e609..95009a73750f 100644
--- a/server/proxy/grpcproxy/adapter/watch_client_adapter.go
+++ b/server/proxy/grpcproxy/adapter/watch_client_adapter.go
@@ -45,24 +45,24 @@ type ws2wcClientStream struct{ chanClientStream }
type ws2wcServerStream struct{ chanServerStream }
func (s *ws2wcClientStream) Send(wr *pb.WatchRequest) error {
- return s.SendMsg(wr)
+ return s.SendMsg(wr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ws2wcClientStream) Recv() (*pb.WatchResponse, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.WatchResponse), nil
}
func (s *ws2wcServerStream) Send(wr *pb.WatchResponse) error {
- return s.SendMsg(wr)
+ return s.SendMsg(wr) //nolint:staticcheck // TODO: remove for a supported version
}
func (s *ws2wcServerStream) Recv() (*pb.WatchRequest, error) {
var v any
- if err := s.RecvMsg(&v); err != nil {
+ if err := s.RecvMsg(&v); err != nil { //nolint:staticcheck // TODO: remove for a supported version
return nil, err
}
return v.(*pb.WatchRequest), nil
diff --git a/server/proxy/grpcproxy/auth.go b/server/proxy/grpcproxy/auth.go
index 753dfa47acfb..f85f8b223d3f 100644
--- a/server/proxy/grpcproxy/auth.go
+++ b/server/proxy/grpcproxy/auth.go
@@ -23,6 +23,8 @@ import (
type AuthProxy struct {
authClient pb.AuthClient
+ // we want compile errors if new methods are added
+ pb.UnsafeAuthServer
}
func NewAuthProxy(c *clientv3.Client) pb.AuthServer {
diff --git a/server/proxy/grpcproxy/cache/store.go b/server/proxy/grpcproxy/cache/store.go
index 69fb38c070f0..9ddd2370ba87 100644
--- a/server/proxy/grpcproxy/cache/store.go
+++ b/server/proxy/grpcproxy/cache/store.go
@@ -20,7 +20,8 @@ import (
"errors"
"sync"
- "github.com/golang/groupcache/lru"
+ "google.golang.org/protobuf/proto"
+ "k8s.io/utils/lru"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
@@ -44,7 +45,7 @@ type Cache interface {
// keyFunc returns the key of a request, which is used to look up its caching response in the cache.
func keyFunc(req *pb.RangeRequest) string {
// TODO: use marshalTo to reduce allocation
- b, err := req.Marshal()
+ b, err := proto.Marshal(req)
if err != nil {
panic(err)
}
diff --git a/server/proxy/grpcproxy/cluster.go b/server/proxy/grpcproxy/cluster.go
index 35b6ce09a9d2..3d478ce44bd5 100644
--- a/server/proxy/grpcproxy/cluster.go
+++ b/server/proxy/grpcproxy/cluster.go
@@ -45,6 +45,9 @@ type clusterProxy struct {
umu sync.RWMutex
umap map[string]endpoints.Endpoint
+
+ // we want compile errors if new methods are added
+ pb.UnsafeClusterServer
}
// NewClusterProxy takes optional prefix to fetch grpc-proxy member endpoints.
@@ -143,7 +146,7 @@ func (cp *clusterProxy) membersFromUpdates() ([]*pb.Member, error) {
defer cp.umu.RUnlock()
mbs := make([]*pb.Member, 0, len(cp.umap))
for _, upt := range cp.umap {
- m, err := decodeMeta(fmt.Sprint(upt.Metadata))
+ m, err := decodeMeta(fmt.Sprint(upt.Metadata)) //nolint:staticcheck // TODO: remove for a supported version
if err != nil {
return nil, err
}
diff --git a/server/proxy/grpcproxy/election.go b/server/proxy/grpcproxy/election.go
index 9ea8d9615760..cf24deec7e16 100644
--- a/server/proxy/grpcproxy/election.go
+++ b/server/proxy/grpcproxy/election.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The etcd Lockors
+// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import (
type electionProxy struct {
electionClient v3electionpb.ElectionClient
+ // we want compile errors if new methods are added
+ v3electionpb.UnsafeElectionServer
}
func NewElectionProxy(client *clientv3.Client) v3electionpb.ElectionServer {
diff --git a/server/proxy/grpcproxy/kv.go b/server/proxy/grpcproxy/kv.go
index 3df361d644e0..3110c1e1050e 100644
--- a/server/proxy/grpcproxy/kv.go
+++ b/server/proxy/grpcproxy/kv.go
@@ -18,6 +18,10 @@ import (
"context"
"errors"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/proto"
+
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/proxy/grpcproxy/cache"
@@ -26,6 +30,8 @@ import (
type kvProxy struct {
kv clientv3.KV
cache cache.Cache
+ // we want compile errors if new methods are added
+ pb.UnsafeKVServer
}
func NewKvProxy(c *clientv3.Client) (pb.KVServer, <-chan struct{}) {
@@ -59,15 +65,20 @@ func (p *kvProxy) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeRespo
}
// cache linearizable as serializable
- req := *r
+ // TODO(fuweid): consider using shadow copy here
+ req := proto.Clone(r).(*pb.RangeRequest)
req.Serializable = true
gresp := (*pb.RangeResponse)(resp.Get())
- p.cache.Add(&req, gresp)
+ p.cache.Add(req, gresp)
cacheKeys.Set(float64(p.cache.Size()))
return gresp, nil
}
+func (p *kvProxy) RangeStream(r *pb.RangeRequest, rs pb.KV_RangeStreamServer) error {
+ return status.Error(codes.Unimplemented, "RangeStream is not supported by the gRPC proxy")
+}
+
func (p *kvProxy) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
p.cache.Invalidate(r.Key, nil)
cacheKeys.Set(float64(p.cache.Size()))
@@ -221,7 +232,7 @@ func TxnRequestToOp(r *pb.TxnRequest) clientv3.Op {
thenops := make([]clientv3.Op, len(r.Success))
elseops := make([]clientv3.Op, len(r.Failure))
for i := range r.Compare {
- cmps[i] = (clientv3.Cmp)(*r.Compare[i])
+ cmps[i] = clientv3.FromCompare(r.Compare[i])
}
for i := range r.Success {
thenops[i] = requestOpToOp(r.Success[i])
diff --git a/server/proxy/grpcproxy/lease.go b/server/proxy/grpcproxy/lease.go
index dc42dc5a0d88..8fd403fa0f4d 100644
--- a/server/proxy/grpcproxy/lease.go
+++ b/server/proxy/grpcproxy/lease.go
@@ -47,6 +47,9 @@ type leaseProxy struct {
// wg waits until all outstanding leaseProxyStream quit.
wg sync.WaitGroup
+
+ // we want compile errors if new methods are added
+ pb.LeaseServer
}
func NewLeaseProxy(ctx context.Context, c *clientv3.Client) (pb.LeaseServer, <-chan struct{}) {
diff --git a/server/proxy/grpcproxy/lock.go b/server/proxy/grpcproxy/lock.go
index 9458080db7b8..275a78f4b80a 100644
--- a/server/proxy/grpcproxy/lock.go
+++ b/server/proxy/grpcproxy/lock.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The etcd Lockors
+// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import (
type lockProxy struct {
lockClient v3lockpb.LockClient
+ // we want compile errors if new methods are added
+ v3lockpb.UnsafeLockServer
}
func NewLockProxy(client *clientv3.Client) v3lockpb.LockServer {
diff --git a/server/proxy/grpcproxy/maintenance.go b/server/proxy/grpcproxy/maintenance.go
index 553b66c4b73b..1174f3d804fa 100644
--- a/server/proxy/grpcproxy/maintenance.go
+++ b/server/proxy/grpcproxy/maintenance.go
@@ -25,6 +25,8 @@ import (
type maintenanceProxy struct {
maintenanceClient pb.MaintenanceClient
+ // we want compile errors if new methods are added
+ pb.UnsafeMaintenanceServer
}
func NewMaintenanceProxy(c *clientv3.Client) pb.MaintenanceServer {
diff --git a/server/proxy/grpcproxy/watch.go b/server/proxy/grpcproxy/watch.go
index 98fd1f78bc55..ebe1e98beabc 100644
--- a/server/proxy/grpcproxy/watch.go
+++ b/server/proxy/grpcproxy/watch.go
@@ -46,6 +46,9 @@ type watchProxy struct {
// kv is used for permission checking
kv clientv3.KV
lg *zap.Logger
+
+ // we want compile errors if new methods are added
+ pb.UnsafeWatchServer
}
func NewWatchProxy(ctx context.Context, lg *zap.Logger, c *clientv3.Client) (pb.WatchServer, <-chan struct{}) {
@@ -269,7 +272,12 @@ func (wps *watchProxyStream) recvLoop() error {
filters: v3rpc.FiltersFromRequest(cr),
}
if !w.wr.valid() {
- w.post(&pb.WatchResponse{WatchId: clientv3.InvalidWatchID, Created: true, Canceled: true})
+ w.post(&pb.WatchResponse{
+ Header: &pb.ResponseHeader{},
+ WatchId: clientv3.InvalidWatchID,
+ Created: true,
+ Canceled: true,
+ })
wps.mu.Unlock()
continue
}
@@ -316,7 +324,7 @@ func (wps *watchProxyStream) delete(id int64) {
wps.ranges.delete(w)
delete(wps.watchers, id)
resp := &pb.WatchResponse{
- Header: &w.lastHeader,
+ Header: w.lastHeader.Clone(),
WatchId: id,
Canceled: true,
}
diff --git a/server/proxy/grpcproxy/watch_broadcasts.go b/server/proxy/grpcproxy/watch_broadcasts.go
index dacd3007d1de..0b99b22cc958 100644
--- a/server/proxy/grpcproxy/watch_broadcasts.go
+++ b/server/proxy/grpcproxy/watch_broadcasts.go
@@ -30,7 +30,7 @@ type watchBroadcasts struct {
donec chan struct{}
}
-// maxCoalesceRecievers prevents a popular watchBroadcast from being coalseced.
+// maxCoalesceRecievers prevents a popular watchBroadcast from being coalesced.
const maxCoalesceReceivers = 5
func newWatchBroadcasts(wp *watchProxy) *watchBroadcasts {
diff --git a/server/proxy/grpcproxy/watcher.go b/server/proxy/grpcproxy/watcher.go
index 08e0ab94c443..7e0f2cc183e7 100644
--- a/server/proxy/grpcproxy/watcher.go
+++ b/server/proxy/grpcproxy/watcher.go
@@ -44,7 +44,7 @@ type watcher struct {
// nextrev is the minimum expected next event revision.
nextrev int64
// lastHeader has the last header sent over the stream.
- lastHeader pb.ResponseHeader
+ lastHeader *pb.ResponseHeader
// wps is the parent.
wps *watchProxyStream
@@ -68,7 +68,7 @@ func (w *watcher) send(wr clientv3.WatchResponse) {
var lastRev int64
for i := range wr.Events {
- ev := (*mvccpb.Event)(wr.Events[i])
+ ev := wr.Events[i]
if ev.Kv.ModRevision < w.nextrev {
continue
}
@@ -79,7 +79,7 @@ func (w *watcher) send(wr clientv3.WatchResponse) {
filtered := false
for _, filter := range w.filters {
- if filter(*ev) {
+ if filter(ev) {
filtered = true
break
}
@@ -89,9 +89,12 @@ func (w *watcher) send(wr clientv3.WatchResponse) {
}
if !w.prevKV {
- evCopy := *ev
- evCopy.PrevKv = nil
- ev = &evCopy
+ evCopy := &mvccpb.Event{
+ Type: ev.Type,
+ Kv: ev.Kv,
+ PrevKv: nil,
+ }
+ ev = evCopy
}
events = append(events, ev)
}
@@ -105,9 +108,9 @@ func (w *watcher) send(wr clientv3.WatchResponse) {
return
}
- w.lastHeader = wr.Header
+ w.lastHeader = wr.Header.Clone()
w.post(&pb.WatchResponse{
- Header: &wr.Header,
+ Header: w.lastHeader.Clone(),
Created: wr.Created,
CompactRevision: wr.CompactRevision,
Canceled: wr.Canceled,
diff --git a/server/storage/backend.go b/server/storage/backend.go
index 7db61f9fae56..513013336ff8 100644
--- a/server/storage/backend.go
+++ b/server/storage/backend.go
@@ -56,8 +56,8 @@ func newBackend(cfg config.ServerConfig, hooks backend.Hooks) backend.Backend {
}
// OpenSnapshotBackend renames a snapshot db to the current etcd db and opens it.
-func OpenSnapshotBackend(cfg config.ServerConfig, ss *snap.Snapshotter, snapshot raftpb.Snapshot, hooks *BackendHooks) (backend.Backend, error) {
- snapPath, err := ss.DBFilePath(snapshot.Metadata.Index)
+func OpenSnapshotBackend(cfg config.ServerConfig, ss *snap.Snapshotter, snapshot *raftpb.Snapshot, hooks *BackendHooks) (backend.Backend, error) {
+ snapPath, err := ss.DBFilePath(snapshot.Metadata.GetIndex())
if err != nil {
return nil, fmt.Errorf("failed to find database snapshot file (%w)", err)
}
@@ -99,16 +99,16 @@ func OpenBackend(cfg config.ServerConfig, hooks backend.Hooks) backend.Backend {
// before updating the backend db after persisting raft snapshot to disk,
// violating the invariant snapshot.Metadata.Index < db.consistentIndex. In this
// case, replace the db with the snapshot db sent by the leader.
-func RecoverSnapshotBackend(cfg config.ServerConfig, oldbe backend.Backend, snapshot raftpb.Snapshot, beExist bool, hooks *BackendHooks) (backend.Backend, error) {
+func RecoverSnapshotBackend(cfg config.ServerConfig, oldbe backend.Backend, snapshot *raftpb.Snapshot, beExist bool, hooks *BackendHooks) (backend.Backend, error) {
consistentIndex := uint64(0)
if beExist {
consistentIndex, _ = schema.ReadConsistentIndex(oldbe.ReadTx())
}
- if snapshot.Metadata.Index <= consistentIndex {
- cfg.Logger.Info("Skipping snapshot backend", zap.Uint64("consistent-index", consistentIndex), zap.Uint64("snapshot-index", snapshot.Metadata.Index))
+ if snapshot.Metadata.GetIndex() <= consistentIndex {
+ cfg.Logger.Info("Skipping snapshot backend", zap.Uint64("consistent-index", consistentIndex), zap.Uint64("snapshot-index", snapshot.Metadata.GetIndex()))
return oldbe, nil
}
- cfg.Logger.Info("Recovering from snapshot backend", zap.Uint64("consistent-index", consistentIndex), zap.Uint64("snapshot-index", snapshot.Metadata.Index))
+ cfg.Logger.Info("Recovering from snapshot backend", zap.Uint64("consistent-index", consistentIndex), zap.Uint64("snapshot-index", snapshot.Metadata.GetIndex()))
oldbe.Close()
return OpenSnapshotBackend(cfg, snap.New(cfg.Logger, cfg.SnapDir()), snapshot, hooks)
}
diff --git a/server/storage/backend/backend.go b/server/storage/backend/backend.go
index 275064f083b2..8f5f76429c74 100644
--- a/server/storage/backend/backend.go
+++ b/server/storage/backend/backend.go
@@ -147,6 +147,9 @@ type BackendConfig struct {
UnsafeNoFsync bool `json:"unsafe-no-fsync"`
// Mlock prevents backend database file to be swapped
Mlock bool
+ // Timeout is the amount of time to wait to obtain a file lock.
+ // When set to zero it will wait indefinitely.
+ Timeout time.Duration
// Hooks are getting executed during lifecycle of Backend's transactions.
Hooks Hooks
@@ -173,6 +176,12 @@ func WithMmapSize(size uint64) BackendConfigOption {
}
}
+func WithTimeout(timeout time.Duration) BackendConfigOption {
+ return func(bcfg *BackendConfig) {
+ bcfg.Timeout = timeout
+ }
+}
+
func NewDefaultBackend(lg *zap.Logger, path string, opts ...BackendConfigOption) Backend {
bcfg := DefaultBackendConfig(lg)
bcfg.Path = path
@@ -199,6 +208,7 @@ func newBackend(bcfg BackendConfig) *backend {
bopts.NoGrowSync = bcfg.UnsafeNoFsync
bopts.Mlock = bcfg.Mlock
bopts.Logger = newBoltLoggerZap(bcfg)
+ bopts.Timeout = bcfg.Timeout
db, err := bolt.Open(bcfg.Path, 0o600, bopts)
if err != nil {
diff --git a/server/storage/backend/batch_tx_test.go b/server/storage/backend/batch_tx_test.go
index 279f199a5dbd..25af69f11e02 100644
--- a/server/storage/backend/batch_tx_test.go
+++ b/server/storage/backend/batch_tx_test.go
@@ -393,9 +393,9 @@ func checkUnsafeForEach(t *testing.T, tx backend.UnsafeReader, expectedKeys, exp
// runWriteback is used test the txWriteBuffer.writeback function, which is called inside tx.Unlock().
// The parameters are chosen based on defaultBatchLimit = 10000
-func runWriteback(t testing.TB, kss, vss [][]string, isSeq bool) {
- b, _ := betesting.NewTmpBackend(t, time.Hour, 10000)
- defer betesting.Close(t, b)
+func runWriteback(tb testing.TB, kss, vss [][]string, isSeq bool) {
+ b, _ := betesting.NewTmpBackend(tb, time.Hour, 10000)
+ defer betesting.Close(tb, b)
tx := b.BatchTx()
diff --git a/server/storage/backend/hooks_test.go b/server/storage/backend/hooks_test.go
index 9a1d33d8253b..52b3ed8ec33a 100644
--- a/server/storage/backend/hooks_test.go
+++ b/server/storage/backend/hooks_test.go
@@ -69,7 +69,7 @@ func write(tx backend.BatchTx, k, v []byte) {
}
func TestBackendAutoCommitBatchIntervalHook(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Minute)
defer cancel()
cfg := backend.DefaultBackendConfig(zaptest.NewLogger(t))
@@ -94,11 +94,11 @@ func TestBackendAutoCommitBatchIntervalHook(t *testing.T) {
waitUntil(ctx, t, func() bool { return getCommitsKey(t, be) == ">ccc" })
}
-func waitUntil(ctx context.Context, t testing.TB, f func() bool) {
+func waitUntil(ctx context.Context, tb testing.TB, f func() bool) {
for !f() {
select {
case <-ctx.Done():
- t.Fatalf("Context cancelled/timedout without condition met: %v", ctx.Err())
+ tb.Fatalf("Context cancelled/timedout without condition met: %v", ctx.Err())
default:
}
time.Sleep(10 * time.Millisecond)
@@ -112,28 +112,28 @@ func prepareBuckenAndKey(tx backend.BatchTx) {
tx.UnsafePut(bucket, key, []byte(">"))
}
-func newTestHooksBackend(t testing.TB, baseConfig backend.BackendConfig) backend.Backend {
+func newTestHooksBackend(tb testing.TB, baseConfig backend.BackendConfig) backend.Backend {
cfg := baseConfig
cfg.Hooks = backend.NewHooks(func(tx backend.UnsafeReadWriter) {
k, v := tx.UnsafeRange(bucket, key, nil, 1)
- t.Logf("OnPreCommit executed: %v %v", string(k[0]), string(v[0]))
- assert.Len(t, k, 1)
- assert.Len(t, v, 1)
+ tb.Logf("OnPreCommit executed: %v %v", string(k[0]), string(v[0]))
+ assert.Len(tb, k, 1)
+ assert.Len(tb, v, 1)
tx.UnsafePut(bucket, key, append(v[0], byte('c')))
})
- be, _ := betesting.NewTmpBackendFromCfg(t, cfg)
- t.Cleanup(func() {
- betesting.Close(t, be)
+ be, _ := betesting.NewTmpBackendFromCfg(tb, cfg)
+ tb.Cleanup(func() {
+ betesting.Close(tb, be)
})
return be
}
-func getCommitsKey(t testing.TB, be backend.Backend) string {
+func getCommitsKey(tb testing.TB, be backend.Backend) string {
rtx := be.BatchTx()
rtx.Lock()
defer rtx.Unlock()
_, v := rtx.UnsafeRange(bucket, key, nil, 1)
- assert.Len(t, v, 1)
+ assert.Len(tb, v, 1)
return string(v[0])
}
diff --git a/server/storage/backend/testing/betesting.go b/server/storage/backend/testing/betesting.go
index e42908f9365d..eae54de9af81 100644
--- a/server/storage/backend/testing/betesting.go
+++ b/server/storage/backend/testing/betesting.go
@@ -26,28 +26,28 @@ import (
"go.etcd.io/etcd/server/v3/storage/backend"
)
-func NewTmpBackendFromCfg(t testing.TB, bcfg backend.BackendConfig) (backend.Backend, string) {
- dir, err := os.MkdirTemp(t.TempDir(), "etcd_backend_test")
+func NewTmpBackendFromCfg(tb testing.TB, bcfg backend.BackendConfig) (backend.Backend, string) {
+ dir, err := os.MkdirTemp(tb.TempDir(), "etcd_backend_test")
if err != nil {
panic(err)
}
tmpPath := filepath.Join(dir, "database")
bcfg.Path = tmpPath
- bcfg.Logger = zaptest.NewLogger(t)
+ bcfg.Logger = zaptest.NewLogger(tb)
return backend.New(bcfg), tmpPath
}
// NewTmpBackend creates a backend implementation for testing.
-func NewTmpBackend(t testing.TB, batchInterval time.Duration, batchLimit int) (backend.Backend, string) {
- bcfg := backend.DefaultBackendConfig(zaptest.NewLogger(t))
+func NewTmpBackend(tb testing.TB, batchInterval time.Duration, batchLimit int) (backend.Backend, string) {
+ bcfg := backend.DefaultBackendConfig(zaptest.NewLogger(tb))
bcfg.BatchInterval, bcfg.BatchLimit = batchInterval, batchLimit
- return NewTmpBackendFromCfg(t, bcfg)
+ return NewTmpBackendFromCfg(tb, bcfg)
}
-func NewDefaultTmpBackend(t testing.TB) (backend.Backend, string) {
- return NewTmpBackendFromCfg(t, backend.DefaultBackendConfig(zaptest.NewLogger(t)))
+func NewDefaultTmpBackend(tb testing.TB) (backend.Backend, string) {
+ return NewTmpBackendFromCfg(tb, backend.DefaultBackendConfig(zaptest.NewLogger(tb)))
}
-func Close(t testing.TB, b backend.Backend) {
- assert.NoError(t, b.Close())
+func Close(tb testing.TB, b backend.Backend) {
+ assert.NoError(tb, b.Close())
}
diff --git a/server/storage/backend/tx_buffer.go b/server/storage/backend/tx_buffer.go
index 821b300bfeff..7aa4c9d32b68 100644
--- a/server/storage/backend/tx_buffer.go
+++ b/server/storage/backend/tx_buffer.go
@@ -17,7 +17,6 @@ package backend
import (
"bytes"
"encoding/hex"
- "fmt"
"sort"
"go.etcd.io/etcd/client/pkg/v3/verify"
@@ -56,17 +55,19 @@ func (txw *txWriteBuffer) put(bucket Bucket, k, v []byte) {
func (txw *txWriteBuffer) putSeq(bucket Bucket, k, v []byte) {
// putSeq is only be called for the data in the Key bucket. The keys
// in the Key bucket should be monotonically increasing revisions.
- verify.Verify(func() {
+ verify.Verify("Broke the rule of monotonically increasing", func() (bool, map[string]any) {
b, ok := txw.buckets[bucket.ID()]
if !ok || b.used == 0 {
- return
+ return true, nil
}
-
existingMaxKey := b.buf[b.used-1].key
if bytes.Compare(k, existingMaxKey) <= 0 {
- panic(fmt.Sprintf("Broke the rule of monotonically increasing, existingMaxKey: %s, currentKey: %s",
- hex.EncodeToString(existingMaxKey), hex.EncodeToString(k)))
+ return false, map[string]any{
+ "existingMaxKey": hex.EncodeToString(existingMaxKey),
+ "currentKey": hex.EncodeToString(k),
+ }
}
+ return true, nil
})
txw.putInternal(bucket, k, v)
}
diff --git a/server/storage/backend/verify.go b/server/storage/backend/verify.go
index 206a8a41d71b..e2f0cd5ba4a2 100644
--- a/server/storage/backend/verify.go
+++ b/server/storage/backend/verify.go
@@ -15,7 +15,6 @@
package backend
import (
- "fmt"
"runtime/debug"
"strings"
@@ -80,9 +79,9 @@ func insideUnittest() bool {
// VerifyBackendConsistency verifies data in ReadTx and BatchTx are consistent.
func VerifyBackendConsistency(b Backend, lg *zap.Logger, skipSafeRangeBucket bool, bucket ...Bucket) {
- verify.Verify(func() {
+ verify.Verify("bucket data mismatch", func() (bool, map[string]any) {
if b == nil {
- return
+ return true, nil
}
if lg != nil {
lg.Debug("verifyBackendConsistency", zap.Bool("skipSafeRangeBucket", skipSafeRangeBucket))
@@ -95,12 +94,15 @@ func VerifyBackendConsistency(b Backend, lg *zap.Logger, skipSafeRangeBucket boo
if skipSafeRangeBucket && bkt.IsSafeRangeBucket() {
continue
}
- unsafeVerifyTxConsistency(b, bkt)
+ if ok, details := unsafeVerifyTxConsistency(b, bkt); !ok {
+ return false, details
+ }
}
+ return true, nil
})
}
-func unsafeVerifyTxConsistency(b Backend, bucket Bucket) {
+func unsafeVerifyTxConsistency(b Backend, bucket Bucket) (bool, map[string]any) {
dataFromWriteTxn := map[string]string{}
b.BatchTx().UnsafeForEach(bucket, func(k, v []byte) error {
dataFromWriteTxn[string(k)] = string(v)
@@ -112,6 +114,12 @@ func unsafeVerifyTxConsistency(b Backend, bucket Bucket) {
return nil
})
if diff := cmp.Diff(dataFromWriteTxn, dataFromReadTxn); diff != "" {
- panic(fmt.Sprintf("bucket %s data mismatch\nwrite TXN: %v\nread TXN: %v\ndiff: %s", bucket.String(), dataFromWriteTxn, dataFromReadTxn, diff))
+ return false, map[string]any{
+ "bucket": bucket.String(),
+ "write TXN": dataFromWriteTxn,
+ "read TXN": dataFromReadTxn,
+ "diff": diff,
+ }
}
+ return true, nil
}
diff --git a/server/storage/hooks.go b/server/storage/hooks.go
index ffec71c0bca2..cdc208d7ed61 100644
--- a/server/storage/hooks.go
+++ b/server/storage/hooks.go
@@ -18,6 +18,7 @@ import (
"sync"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -30,7 +31,7 @@ type BackendHooks struct {
lg *zap.Logger
// confState to Be written in the next submitted Backend transaction (if dirty)
- confState raftpb.ConfState
+ confState *raftpb.ConfState
// first write changes it to 'dirty'. false by default, so
// not initialized `confState` is meaningless.
confStateDirty bool
@@ -46,7 +47,7 @@ func (bh *BackendHooks) OnPreCommitUnsafe(tx backend.UnsafeReadWriter) {
bh.confStateLock.Lock()
defer bh.confStateLock.Unlock()
if bh.confStateDirty {
- schema.MustUnsafeSaveConfStateToBackend(bh.lg, tx, &bh.confState)
+ schema.MustUnsafeSaveConfStateToBackend(bh.lg, tx, bh.confState)
// save bh.confState
bh.confStateDirty = false
}
@@ -55,6 +56,6 @@ func (bh *BackendHooks) OnPreCommitUnsafe(tx backend.UnsafeReadWriter) {
func (bh *BackendHooks) SetConfState(confState *raftpb.ConfState) {
bh.confStateLock.Lock()
defer bh.confStateLock.Unlock()
- bh.confState = *confState
+ bh.confState = proto.Clone(confState).(*raftpb.ConfState)
bh.confStateDirty = true
}
diff --git a/server/storage/mvcc/hash_test.go b/server/storage/mvcc/hash_test.go
index d08b3ad19821..9555293aaa9d 100644
--- a/server/storage/mvcc/hash_test.go
+++ b/server/storage/mvcc/hash_test.go
@@ -138,7 +138,7 @@ func TestCompactionHash(t *testing.T) {
s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
defer cleanup(s, b)
- testutil.TestCompactionHash(context.Background(), t, hashTestCase{s}, s.cfg.CompactionBatchLimit)
+ testutil.TestCompactionHash(t.Context(), t, hashTestCase{s}, s.cfg.CompactionBatchLimit)
}
type hashTestCase struct {
diff --git a/server/storage/mvcc/index.go b/server/storage/mvcc/index.go
index f300831b2936..235c225f1221 100644
--- a/server/storage/mvcc/index.go
+++ b/server/storage/mvcc/index.go
@@ -17,14 +17,14 @@ package mvcc
import (
"sync"
- "github.com/google/btree"
"go.uber.org/zap"
+ "k8s.io/utils/third_party/forked/golang/btree"
)
type index interface {
Get(key []byte, atRev int64) (rev, created Revision, ver int64, err error)
- Range(key, end []byte, atRev int64) ([][]byte, []Revision)
- Revisions(key, end []byte, atRev int64, limit int) ([]Revision, int)
+ Range(key, end []byte, atRev int64, limit int, withTotalCount bool) (keys [][]byte, modifies, creates []Revision, versions []int64, totalCount int)
+ Revisions(key, end []byte, atRev int64, limit int, withTotalCount bool) ([]Revision, int)
CountRevisions(key, end []byte, atRev int64) int
Put(key []byte, rev Revision)
Tombstone(key []byte, rev Revision) error
@@ -38,13 +38,13 @@ type index interface {
type treeIndex struct {
sync.RWMutex
- tree *btree.BTreeG[*keyIndex]
+ tree *btree.BTree[*keyIndex]
lg *zap.Logger
}
func newTreeIndex(lg *zap.Logger) index {
return &treeIndex{
- tree: btree.NewG(32, func(aki *keyIndex, bki *keyIndex) bool {
+ tree: btree.New(32, func(aki *keyIndex, bki *keyIndex) bool {
return aki.Less(bki)
}),
lg: lg,
@@ -109,7 +109,7 @@ func (ti *treeIndex) unsafeVisit(key, end []byte, f func(ki *keyIndex) bool) {
// Revisions returns limited number of revisions from key(included) to end(excluded)
// at the given rev. The returned slice is sorted in the order of key. There is no limit if limit <= 0.
// The second return parameter isn't capped by the limit and reflects the total number of revisions.
-func (ti *treeIndex) Revisions(key, end []byte, atRev int64, limit int) (revs []Revision, total int) {
+func (ti *treeIndex) Revisions(key, end []byte, atRev int64, limit int, withTotalCount bool) (revs []Revision, totalCount int) {
ti.RLock()
defer ti.RUnlock()
@@ -121,15 +121,19 @@ func (ti *treeIndex) Revisions(key, end []byte, atRev int64, limit int) (revs []
return []Revision{rev}, 1
}
ti.unsafeVisit(key, end, func(ki *keyIndex) bool {
+ reachedLimit := limit > 0 && len(revs) >= limit
+ if reachedLimit && !withTotalCount {
+ return false
+ }
if rev, _, _, err := ki.get(ti.lg, atRev); err == nil {
- if limit <= 0 || len(revs) < limit {
+ if !reachedLimit {
revs = append(revs, rev)
}
- total++
+ totalCount++
}
return true
})
- return revs, total
+ return revs, totalCount
}
// CountRevisions returns the number of revisions
@@ -155,25 +159,34 @@ func (ti *treeIndex) CountRevisions(key, end []byte, atRev int64) int {
return total
}
-func (ti *treeIndex) Range(key, end []byte, atRev int64) (keys [][]byte, revs []Revision) {
+func (ti *treeIndex) Range(key, end []byte, atRev int64, limit int, withTotalCount bool) (keys [][]byte, modifies, creates []Revision, versions []int64, totalCount int) {
ti.RLock()
defer ti.RUnlock()
if end == nil {
- rev, _, _, err := ti.unsafeGet(key, atRev)
+ modified, created, version, err := ti.unsafeGet(key, atRev)
if err != nil {
- return nil, nil
+ return nil, nil, nil, nil, 0
}
- return [][]byte{key}, []Revision{rev}
+ return [][]byte{key}, []Revision{modified}, []Revision{created}, []int64{version}, 1
}
ti.unsafeVisit(key, end, func(ki *keyIndex) bool {
- if rev, _, _, err := ki.get(ti.lg, atRev); err == nil {
- revs = append(revs, rev)
- keys = append(keys, ki.key)
+ reachedLimit := limit > 0 && len(keys) >= limit
+ if reachedLimit && !withTotalCount {
+ return false
+ }
+ if modified, created, version, err := ki.get(ti.lg, atRev); err == nil {
+ if !reachedLimit {
+ modifies = append(modifies, modified)
+ keys = append(keys, ki.key)
+ creates = append(creates, created)
+ versions = append(versions, version)
+ }
+ totalCount++
}
return true
})
- return keys, revs
+ return keys, modifies, creates, versions, totalCount
}
func (ti *treeIndex) Tombstone(key []byte, rev Revision) error {
diff --git a/server/storage/mvcc/index_test.go b/server/storage/mvcc/index_test.go
index d7f32fded2d8..7a093c911e6e 100644
--- a/server/storage/mvcc/index_test.go
+++ b/server/storage/mvcc/index_test.go
@@ -73,51 +73,208 @@ func TestIndexRange(t *testing.T) {
atRev := int64(3)
tests := []struct {
- key, end []byte
- wkeys [][]byte
- wrevs []Revision
+ name string
+ rangeStart, rangeEnd []byte
+ rangeLimit int
+ withTotalCount bool
+ expectKeys [][]byte
+ expectRevisions []Revision
+ expectTotalCount int
}{
- // single key that not found
- {
- []byte("bar"), nil, nil, nil,
- },
- // single key that found
- {
- []byte("foo"), nil, allKeys[:1], allRevs[:1],
- },
- // range keys, return first member
- {
- []byte("foo"), []byte("foo1"), allKeys[:1], allRevs[:1],
- },
- // range keys, return first two members
{
- []byte("foo"), []byte("foo2"), allKeys[:2], allRevs[:2],
- },
- // range keys, return all members
- {
- []byte("foo"), []byte("fop"), allKeys, allRevs,
- },
- // range keys, return last two members
- {
- []byte("foo1"), []byte("fop"), allKeys[1:], allRevs[1:],
- },
- // range keys, return last member
- {
- []byte("foo2"), []byte("fop"), allKeys[2:], allRevs[2:],
- },
- // range keys, return nothing
- {
- []byte("foo3"), []byte("fop"), nil, nil,
+ name: "single key that not found",
+ rangeStart: []byte("bar"),
+ rangeEnd: nil,
+ expectKeys: nil,
+ expectRevisions: nil,
+ expectTotalCount: 0,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "single key that found",
+ rangeStart: []byte("foo"),
+ rangeEnd: nil,
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 1,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys, return first member",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("foo1"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 1,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys, return first two members",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("foo2"),
+ expectKeys: allKeys[:2],
+ expectRevisions: allRevs[:2],
+ expectTotalCount: 2,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys, return all members",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys,
+ expectRevisions: allRevs,
+ expectTotalCount: 3,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys limiting to one result without total count, return the first member",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 1,
+ rangeLimit: 1,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys with limit equal the index size without total count, return all members",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys,
+ expectRevisions: allRevs,
+ expectTotalCount: 3,
+ rangeLimit: 3,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys with over limit, without total count, return all members",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys,
+ expectRevisions: allRevs,
+ expectTotalCount: 3,
+ rangeLimit: 4,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys limiting to one result with total count, return the first member and all total count",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 3,
+ rangeLimit: 1,
+ withTotalCount: true,
+ },
+ {
+ name: "range keys with limit on non-existent keys, return nothing",
+ rangeStart: []byte("fo"),
+ rangeEnd: []byte("foo"),
+ expectKeys: nil,
+ expectRevisions: nil,
+ expectTotalCount: 0,
+ rangeLimit: 3,
+ withTotalCount: true,
+ },
+ {
+ name: "range keys with more limit than the one key found, returning the key",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("foo1"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 1,
+ rangeLimit: 3,
+ withTotalCount: true,
+ },
+ {
+ name: "range of 1 keys with 1 limit and total count, returning 1 key with 1 count",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("foo1"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 1,
+ rangeLimit: 1,
+ withTotalCount: true,
+ },
+ {
+ name: "range of 3 keys with 1 limit and total count, returning 1 key with 3 count",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("foo3"),
+ expectKeys: allKeys[:1],
+ expectRevisions: allRevs[:1],
+ expectTotalCount: 3,
+ rangeLimit: 1,
+ withTotalCount: true,
+ },
+ {
+ name: "range of 2 keys not starting from the beginning with 1 limit and total count, returning 1 key with 2 count",
+ rangeStart: []byte("foo1"),
+ rangeEnd: []byte("foo3"),
+ expectKeys: allKeys[1:2],
+ expectRevisions: allRevs[1:2],
+ expectTotalCount: 2,
+ rangeLimit: 1,
+ withTotalCount: true,
+ },
+ {
+ name: "range of all keys with equal-sized limit and total count, returning all keys with the same count",
+ rangeStart: []byte("foo"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys,
+ expectRevisions: allRevs,
+ expectTotalCount: 3,
+ rangeLimit: 3,
+ withTotalCount: true,
+ },
+ {
+ name: "range keys, return last two members",
+ rangeStart: []byte("foo1"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys[1:],
+ expectRevisions: allRevs[1:],
+ expectTotalCount: 2,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys, return last member",
+ rangeStart: []byte("foo2"),
+ rangeEnd: []byte("fop"),
+ expectKeys: allKeys[2:],
+ expectRevisions: allRevs[2:],
+ expectTotalCount: 1,
+ rangeLimit: 0,
+ withTotalCount: false,
+ },
+ {
+ name: "range keys, return nothing",
+ rangeStart: []byte("foo3"),
+ rangeEnd: []byte("fop"),
+ expectKeys: nil,
+ expectRevisions: nil,
+ expectTotalCount: 0,
+ rangeLimit: 0,
+ withTotalCount: false,
},
}
for i, tt := range tests {
- keys, revs := ti.Range(tt.key, tt.end, atRev)
- if !reflect.DeepEqual(keys, tt.wkeys) {
- t.Errorf("#%d: keys = %+v, want %+v", i, keys, tt.wkeys)
- }
- if !reflect.DeepEqual(revs, tt.wrevs) {
- t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.wrevs)
- }
+ t.Run(tt.name, func(t *testing.T) {
+ keys, revs, _, _, total := ti.Range(tt.rangeStart, tt.rangeEnd, atRev, tt.rangeLimit, tt.withTotalCount)
+ if !reflect.DeepEqual(keys, tt.expectKeys) {
+ t.Errorf("#%d: keys = %+v, want %+v", i, keys, tt.expectKeys)
+ }
+ if !reflect.DeepEqual(revs, tt.expectRevisions) {
+ t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.expectRevisions)
+ }
+ if total != tt.expectTotalCount {
+ t.Errorf("#%d: total = %+d, want %+d", i, total, tt.expectTotalCount)
+ }
+ })
}
}
@@ -223,7 +380,7 @@ func TestIndexRevision(t *testing.T) {
},
}
for i, tt := range tests {
- revs, _ := ti.Revisions(tt.key, tt.end, tt.atRev, tt.limit)
+ revs, _ := ti.Revisions(tt.key, tt.end, tt.atRev, tt.limit, true)
if !reflect.DeepEqual(revs, tt.wrevs) {
t.Errorf("#%d limit %d: revs = %+v, want %+v", i, tt.limit, revs, tt.wrevs)
}
@@ -234,6 +391,99 @@ func TestIndexRevision(t *testing.T) {
}
}
+func TestIndexRevisionsWithTotalCount(t *testing.T) {
+ allKeys := [][]byte{[]byte("foo"), []byte("foo1"), []byte("foo2"), []byte("foo2"), []byte("foo1"), []byte("foo")}
+ allRevs := []Revision{{Main: 1}, {Main: 2}, {Main: 3}, {Main: 4}, {Main: 5}, {Main: 6}}
+
+ ti := newTreeIndex(zaptest.NewLogger(t))
+ for i := range allKeys {
+ ti.Put(allKeys[i], allRevs[i])
+ }
+
+ // Range [foo, fop) @ rev 6 matches 3 keys: foo, foo1, foo2.
+ tests := []struct {
+ name string
+ key, end []byte
+ atRev int64
+ limit int
+ withTotalCount bool
+ wrevs []Revision
+ wtotal int
+ }{
+ {
+ name: "withTotalCount=true with limit returns the full total",
+ key: []byte("foo"),
+ end: []byte("fop"),
+ atRev: 6,
+ limit: 1,
+ withTotalCount: true,
+ wrevs: []Revision{{Main: 6}},
+ wtotal: 3,
+ },
+ {
+ name: "withTotalCount=false with limit caps total at len(revs)",
+ key: []byte("foo"),
+ end: []byte("fop"),
+ atRev: 6,
+ limit: 1,
+ withTotalCount: false,
+ wrevs: []Revision{{Main: 6}},
+ wtotal: 1,
+ },
+ {
+ name: "withTotalCount=false with limit larger than matches returns full total",
+ key: []byte("foo"),
+ end: []byte("fop"),
+ atRev: 6,
+ limit: 10,
+ withTotalCount: false,
+ wrevs: []Revision{{Main: 6}, {Main: 5}, {Main: 4}},
+ wtotal: 3,
+ },
+ {
+ name: "withTotalCount=false without limit returns full total",
+ key: []byte("foo"),
+ end: []byte("fop"),
+ atRev: 6,
+ limit: 0,
+ withTotalCount: false,
+ wrevs: []Revision{{Main: 6}, {Main: 5}, {Main: 4}},
+ wtotal: 3,
+ },
+ {
+ name: "withTotalCount=true without limit returns full total",
+ key: []byte("foo"),
+ end: []byte("fop"),
+ atRev: 6,
+ limit: 0,
+ withTotalCount: true,
+ wrevs: []Revision{{Main: 6}, {Main: 5}, {Main: 4}},
+ wtotal: 3,
+ },
+ {
+ name: "withTotalCount=false with no matches",
+ key: []byte("bar"),
+ end: []byte("baz"),
+ atRev: 6,
+ limit: 5,
+ withTotalCount: false,
+ wrevs: nil,
+ wtotal: 0,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ revs, total := ti.Revisions(tt.key, tt.end, tt.atRev, tt.limit, tt.withTotalCount)
+ if !reflect.DeepEqual(revs, tt.wrevs) {
+ t.Errorf("revs = %+v, want %+v", revs, tt.wrevs)
+ }
+ if total != tt.wtotal {
+ t.Errorf("total = %d, want %d", total, tt.wtotal)
+ }
+ })
+ }
+}
+
func TestIndexCompactAndKeep(t *testing.T) {
maxRev := int64(20)
diff --git a/server/storage/mvcc/kv.go b/server/storage/mvcc/kv.go
index 6250bb91198b..41620279d74a 100644
--- a/server/storage/mvcc/kv.go
+++ b/server/storage/mvcc/kv.go
@@ -24,13 +24,15 @@ import (
)
type RangeOptions struct {
- Limit int64
- Rev int64
- Count bool
+ Limit int64
+ Rev int64
+ CountOnly bool
+ FastKeysOnly bool
+ WithTotalCount bool
}
type RangeResult struct {
- KVs []mvccpb.KeyValue
+ KVs []*mvccpb.KeyValue
Rev int64
Count int
}
@@ -86,7 +88,7 @@ type TxnWrite interface {
TxnRead
WriteView
// Changes gets the changes made since opening the write txn.
- Changes() []mvccpb.KeyValue
+ Changes() []*mvccpb.KeyValue
}
// txnReadWrite coerces a read txn to a write, panicking on any write operation.
@@ -96,7 +98,7 @@ func (trw *txnReadWrite) DeleteRange(key, end []byte) (n, rev int64) { panic("un
func (trw *txnReadWrite) Put(key, value []byte, lease lease.LeaseID) (rev int64) {
panic("unexpected Put")
}
-func (trw *txnReadWrite) Changes() []mvccpb.KeyValue { return nil }
+func (trw *txnReadWrite) Changes() []*mvccpb.KeyValue { return nil }
func NewReadOnlyTxnWrite(txn TxnRead) TxnWrite { return &txnReadWrite{txn} }
diff --git a/server/storage/mvcc/kv_test.go b/server/storage/mvcc/kv_test.go
index 790b534395b0..8bfe81820bfe 100644
--- a/server/storage/mvcc/kv_test.go
+++ b/server/storage/mvcc/kv_test.go
@@ -19,14 +19,15 @@ import (
"errors"
"fmt"
"os"
- "reflect"
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/assert"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -89,7 +90,7 @@ func testKVRange(t *testing.T, f rangeFunc) {
wrev := int64(4)
tests := []struct {
key, end []byte
- wkvs []mvccpb.KeyValue
+ wkvs []*mvccpb.KeyValue
}{
// get no keys
{
@@ -136,7 +137,7 @@ func testKVRange(t *testing.T, f rangeFunc) {
if r.Rev != wrev {
t.Errorf("#%d: rev = %d, want %d", i, r.Rev, wrev)
}
- if !reflect.DeepEqual(r.KVs, tt.wkvs) {
+ if !cmp.Equal(r.KVs, tt.wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, tt.wkvs)
}
}
@@ -155,7 +156,7 @@ func testKVRangeRev(t *testing.T, f rangeFunc) {
tests := []struct {
rev int64
wrev int64
- wkvs []mvccpb.KeyValue
+ wkvs []*mvccpb.KeyValue
}{
{-1, 4, kvs},
{0, 4, kvs},
@@ -172,7 +173,7 @@ func testKVRangeRev(t *testing.T, f rangeFunc) {
if r.Rev != tt.wrev {
t.Errorf("#%d: rev = %d, want %d", i, r.Rev, tt.wrev)
}
- if !reflect.DeepEqual(r.KVs, tt.wkvs) {
+ if !cmp.Equal(r.KVs, tt.wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, tt.wkvs)
}
}
@@ -225,7 +226,7 @@ func testKVRangeLimit(t *testing.T, f rangeFunc) {
tests := []struct {
limit int64
wcounts int64
- wkvs []mvccpb.KeyValue
+ wkvs []*mvccpb.KeyValue
}{
// no limit
{-1, 3, kvs},
@@ -237,11 +238,11 @@ func testKVRangeLimit(t *testing.T, f rangeFunc) {
{100, 3, kvs},
}
for i, tt := range tests {
- r, err := f(s, []byte("foo"), []byte("foo3"), RangeOptions{Limit: tt.limit})
+ r, err := f(s, []byte("foo"), []byte("foo3"), RangeOptions{Limit: tt.limit, WithTotalCount: true})
if err != nil {
t.Fatalf("#%d: range error (%v)", i, err)
}
- if !reflect.DeepEqual(r.KVs, tt.wkvs) {
+ if !cmp.Equal(r.KVs, tt.wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, tt.wkvs)
}
if r.Rev != wrev {
@@ -273,14 +274,14 @@ func testKVPutMultipleTimes(t *testing.T, f putFunc) {
t.Errorf("#%d: rev = %d, want %d", i, rev, base+1)
}
- r, err := s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{})
+ r, err := s.Range(t.Context(), []byte("foo"), nil, RangeOptions{})
if err != nil {
t.Fatal(err)
}
- wkvs := []mvccpb.KeyValue{
+ wkvs := []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: base + 1, Version: base, Lease: base},
}
- if !reflect.DeepEqual(r.KVs, wkvs) {
+ if !cmp.Equal(r.KVs, wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, wkvs)
}
}
@@ -384,14 +385,14 @@ func testKVPutWithSameLease(t *testing.T, f putFunc) {
}
// check leaseID
- r, err := s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{})
+ r, err := s.Range(t.Context(), []byte("foo"), nil, RangeOptions{})
if err != nil {
t.Fatal(err)
}
- wkvs := []mvccpb.KeyValue{
+ wkvs := []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 3, Version: 2, Lease: leaseID},
}
- if !reflect.DeepEqual(r.KVs, wkvs) {
+ if !cmp.Equal(r.KVs, wkvs, protocmp.Transform()) {
t.Errorf("kvs = %+v, want %+v", r.KVs, wkvs)
}
}
@@ -412,14 +413,14 @@ func TestKVOperationInSequence(t *testing.T) {
t.Errorf("#%d: put rev = %d, want %d", i, rev, base+1)
}
- r, err := s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
+ r, err := s.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
if err != nil {
t.Fatal(err)
}
- wkvs := []mvccpb.KeyValue{
+ wkvs := []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: base + 1, ModRevision: base + 1, Version: 1, Lease: int64(lease.NoLease)},
}
- if !reflect.DeepEqual(r.KVs, wkvs) {
+ if !cmp.Equal(r.KVs, wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, wkvs)
}
if r.Rev != base+1 {
@@ -432,7 +433,7 @@ func TestKVOperationInSequence(t *testing.T) {
t.Errorf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, 1, base+2)
}
- r, err = s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: base + 2})
+ r, err = s.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: base + 2})
if err != nil {
t.Fatal(err)
}
@@ -490,7 +491,7 @@ func TestKVTxnNonBlockRange(t *testing.T) {
donec := make(chan struct{})
go func() {
defer close(donec)
- s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{})
+ s.Range(t.Context(), []byte("foo"), nil, RangeOptions{})
}()
select {
case <-donec:
@@ -516,14 +517,14 @@ func TestKVTxnOperationInSequence(t *testing.T) {
t.Errorf("#%d: put rev = %d, want %d", i, rev, base+1)
}
- r, err := txn.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
+ r, err := txn.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
if err != nil {
t.Fatal(err)
}
- wkvs := []mvccpb.KeyValue{
+ wkvs := []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: base + 1, ModRevision: base + 1, Version: 1, Lease: int64(lease.NoLease)},
}
- if !reflect.DeepEqual(r.KVs, wkvs) {
+ if !cmp.Equal(r.KVs, wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, wkvs)
}
if r.Rev != base+1 {
@@ -536,7 +537,7 @@ func TestKVTxnOperationInSequence(t *testing.T) {
t.Errorf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, 1, base+1)
}
- r, err = txn.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
+ r, err = txn.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: base + 1})
if err != nil {
t.Errorf("#%d: range error (%v)", i, err)
}
@@ -565,17 +566,17 @@ func TestKVCompactReserveLastValue(t *testing.T) {
tests := []struct {
rev int64
// wanted kvs right after the compacted rev
- wkvs []mvccpb.KeyValue
+ wkvs []*mvccpb.KeyValue
}{
{
1,
- []mvccpb.KeyValue{
+ []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar0"), CreateRevision: 2, ModRevision: 2, Version: 1, Lease: 1},
},
},
{
2,
- []mvccpb.KeyValue{
+ []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 3, Version: 2, Lease: 2},
},
},
@@ -585,7 +586,7 @@ func TestKVCompactReserveLastValue(t *testing.T) {
},
{
4,
- []mvccpb.KeyValue{
+ []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar2"), CreateRevision: 5, ModRevision: 5, Version: 1, Lease: 3},
},
},
@@ -593,13 +594,13 @@ func TestKVCompactReserveLastValue(t *testing.T) {
for i, tt := range tests {
_, err := s.Compact(traceutil.TODO(), tt.rev)
if err != nil {
- t.Errorf("#%d: unexpect compact error %v", i, err)
+ t.Errorf("#%d: unexpected compact error %v", i, err)
}
- r, err := s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: tt.rev + 1})
+ r, err := s.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: tt.rev + 1})
if err != nil {
- t.Errorf("#%d: unexpect range error %v", i, err)
+ t.Errorf("#%d: unexpected range error %v", i, err)
}
- if !reflect.DeepEqual(r.KVs, tt.wkvs) {
+ if !cmp.Equal(r.KVs, tt.wkvs, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, r.KVs, tt.wkvs)
}
}
@@ -695,9 +696,9 @@ func TestKVRestore(t *testing.T) {
b, _ := betesting.NewDefaultTmpBackend(t)
s := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{CompactionBatchLimit: compactBatchLimit})
tt(s)
- var kvss [][]mvccpb.KeyValue
+ var kvss [][]*mvccpb.KeyValue
for k := int64(0); k < 10; k++ {
- r, _ := s.Range(context.TODO(), []byte("a"), []byte("z"), RangeOptions{Rev: k})
+ r, _ := s.Range(t.Context(), []byte("a"), []byte("z"), RangeOptions{Rev: k})
kvss = append(kvss, r.KVs)
}
@@ -713,14 +714,14 @@ func TestKVRestore(t *testing.T) {
// wait for possible compaction to finish
testutil.WaitSchedule()
- var nkvss [][]mvccpb.KeyValue
+ var nkvss [][]*mvccpb.KeyValue
for k := int64(0); k < 10; k++ {
- r, _ := ns.Range(context.TODO(), []byte("a"), []byte("z"), RangeOptions{Rev: k})
+ r, _ := ns.Range(t.Context(), []byte("a"), []byte("z"), RangeOptions{Rev: k})
nkvss = append(nkvss, r.KVs)
}
cleanup(ns, b)
- if !reflect.DeepEqual(nkvss, kvss) {
+ if !cmp.Equal(nkvss, kvss, protocmp.Transform()) {
t.Errorf("#%d: kvs history = %+v, want %+v", i, nkvss, kvss)
}
}
@@ -759,11 +760,11 @@ func TestKVSnapshot(t *testing.T) {
ns := NewStore(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
defer ns.Close()
- r, err := ns.Range(context.TODO(), []byte("a"), []byte("z"), RangeOptions{})
+ r, err := ns.Range(t.Context(), []byte("a"), []byte("z"), RangeOptions{})
if err != nil {
- t.Errorf("unexpect range error (%v)", err)
+ t.Errorf("unexpected range error (%v)", err)
}
- if !reflect.DeepEqual(r.KVs, wkvs) {
+ if !cmp.Equal(r.KVs, wkvs, protocmp.Transform()) {
t.Errorf("kvs = %+v, want %+v", r.KVs, wkvs)
}
if r.Rev != 4 {
@@ -779,11 +780,11 @@ func TestWatchableKVWatch(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- wid, _ := w.Watch(0, []byte("foo"), []byte("fop"), 0)
+ wid, _ := w.Watch(t.Context(), 0, []byte("foo"), []byte("fop"), 0)
- wev := []mvccpb.Event{
+ wev := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
@@ -794,7 +795,7 @@ func TestWatchableKVWatch(t *testing.T) {
},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: []byte("foo1"),
Value: []byte("bar1"),
@@ -805,7 +806,7 @@ func TestWatchableKVWatch(t *testing.T) {
},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: []byte("foo1"),
Value: []byte("bar11"),
@@ -824,7 +825,7 @@ func TestWatchableKVWatch(t *testing.T) {
t.Errorf("resp.WatchID got = %d, want = %d", resp.WatchID, wid)
}
ev := resp.Events[0]
- if !reflect.DeepEqual(ev, wev[0]) {
+ if !cmp.Equal(ev, wev[0], protocmp.Transform()) {
t.Errorf("watched event = %+v, want %+v", ev, wev[0])
}
case <-time.After(5 * time.Second):
@@ -839,7 +840,7 @@ func TestWatchableKVWatch(t *testing.T) {
t.Errorf("resp.WatchID got = %d, want = %d", resp.WatchID, wid)
}
ev := resp.Events[0]
- if !reflect.DeepEqual(ev, wev[1]) {
+ if !cmp.Equal(ev, wev[1], protocmp.Transform()) {
t.Errorf("watched event = %+v, want %+v", ev, wev[1])
}
case <-time.After(5 * time.Second):
@@ -847,7 +848,7 @@ func TestWatchableKVWatch(t *testing.T) {
}
w = s.NewWatchStream()
- wid, _ = w.Watch(0, []byte("foo1"), []byte("foo2"), 3)
+ wid, _ = w.Watch(t.Context(), 0, []byte("foo1"), []byte("foo2"), 3)
select {
case resp := <-w.Chan():
@@ -855,7 +856,7 @@ func TestWatchableKVWatch(t *testing.T) {
t.Errorf("resp.WatchID got = %d, want = %d", resp.WatchID, wid)
}
ev := resp.Events[0]
- if !reflect.DeepEqual(ev, wev[1]) {
+ if !cmp.Equal(ev, wev[1], protocmp.Transform()) {
t.Errorf("watched event = %+v, want %+v", ev, wev[1])
}
case <-time.After(5 * time.Second):
@@ -869,7 +870,7 @@ func TestWatchableKVWatch(t *testing.T) {
t.Errorf("resp.WatchID got = %d, want = %d", resp.WatchID, wid)
}
ev := resp.Events[0]
- if !reflect.DeepEqual(ev, wev[2]) {
+ if !cmp.Equal(ev, wev[2], protocmp.Transform()) {
t.Errorf("watched event = %+v, want %+v", ev, wev[2])
}
case <-time.After(5 * time.Second):
@@ -882,11 +883,11 @@ func cleanup(s KV, b backend.Backend) {
b.Close()
}
-func put3TestKVs(s KV) []mvccpb.KeyValue {
+func put3TestKVs(s KV) []*mvccpb.KeyValue {
s.Put([]byte("foo"), []byte("bar"), 1)
s.Put([]byte("foo1"), []byte("bar1"), 2)
s.Put([]byte("foo2"), []byte("bar2"), 3)
- return []mvccpb.KeyValue{
+ return []*mvccpb.KeyValue{
{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1, Lease: 1},
{Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 3, ModRevision: 3, Version: 1, Lease: 2},
{Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 4, ModRevision: 4, Version: 1, Lease: 3},
diff --git a/server/storage/mvcc/kvstore.go b/server/storage/mvcc/kvstore.go
index 77c50226257c..a9cb65c0c9ff 100644
--- a/server/storage/mvcc/kvstore.go
+++ b/server/storage/mvcc/kvstore.go
@@ -23,6 +23,7 @@ import (
"time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/pkg/v3/verify"
@@ -426,7 +427,7 @@ func (s *store) restore() error {
type revKeyValue struct {
key []byte
- kv mvccpb.KeyValue
+ kv *mvccpb.KeyValue
kstr string
}
@@ -459,9 +460,10 @@ func restoreIntoIndex(lg *zap.Logger, idx index) (chan<- revKeyValue, <-chan int
}
rev := BytesToRev(rkv.key)
- verify.Verify(func() {
- if rev.Main < currentRev {
- panic(fmt.Errorf("revision %d shouldn't be less than the previous revision %d", rev.Main, currentRev))
+ verify.Verify("revision shouldn't be less than the previous revision", func() (bool, map[string]any) {
+ return rev.Main >= currentRev, map[string]any{
+ "revision": rev.Main,
+ "previous revision": currentRev,
}
})
currentRev = rev.Main
@@ -491,9 +493,13 @@ func restoreIntoIndex(lg *zap.Logger, idx index) (chan<- revKeyValue, <-chan int
func restoreChunk(lg *zap.Logger, kvc chan<- revKeyValue, keys, vals [][]byte, keyToLease map[string]lease.LeaseID) {
for i, key := range keys {
rkv := revKeyValue{key: key}
- if err := rkv.kv.Unmarshal(vals[i]); err != nil {
+
+ kv := &mvccpb.KeyValue{}
+ if err := proto.Unmarshal(vals[i], kv); err != nil {
lg.Fatal("failed to unmarshal mvccpb.KeyValue", zap.Error(err))
}
+ rkv.kv = kv
+
rkv.kstr = string(rkv.kv.Key)
if isTombstone(key) {
delete(keyToLease, rkv.kstr)
diff --git a/server/storage/mvcc/kvstore_bench_test.go b/server/storage/mvcc/kvstore_bench_test.go
index f7a9bd7296c3..024fc7dd10d8 100644
--- a/server/storage/mvcc/kvstore_bench_test.go
+++ b/server/storage/mvcc/kvstore_bench_test.go
@@ -15,7 +15,6 @@
package mvcc
import (
- "context"
"testing"
"github.com/stretchr/testify/require"
@@ -70,7 +69,7 @@ func benchmarkStoreRange(b *testing.B, n int) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- s.Range(context.TODO(), begin, end, RangeOptions{})
+ s.Range(b.Context(), begin, end, RangeOptions{})
}
}
diff --git a/server/storage/mvcc/kvstore_compaction.go b/server/storage/mvcc/kvstore_compaction.go
index a052c93943e0..6f8683155cb5 100644
--- a/server/storage/mvcc/kvstore_compaction.go
+++ b/server/storage/mvcc/kvstore_compaction.go
@@ -49,6 +49,7 @@ func (s *store) scheduleCompaction(compactMainRev, prevCompactRev int64) (KeyVal
tx := s.b.BatchTx()
tx.LockOutsideApply()
+ // gofail: var compactAfterAcquiredBatchTxLock struct{}
keys, values := tx.UnsafeRange(schema.Key, last, end, int64(batchNum))
for i := range keys {
rev = BytesToRev(keys[i])
@@ -71,6 +72,7 @@ func (s *store) scheduleCompaction(compactMainRev, prevCompactRev int64) (KeyVal
"finished scheduled compaction",
zap.Int64("compact-revision", compactMainRev),
zap.Duration("took", time.Since(totalStart)),
+ zap.Int("number-of-keys-compacted", keyCompactions),
zap.Uint32("hash", hash.Hash),
zap.Int64("current-db-size-bytes", size),
zap.String("current-db-size", humanize.Bytes(uint64(size))),
diff --git a/server/storage/mvcc/kvstore_compaction_test.go b/server/storage/mvcc/kvstore_compaction_test.go
index b4a7f41a3677..d61a1def75e8 100644
--- a/server/storage/mvcc/kvstore_compaction_test.go
+++ b/server/storage/mvcc/kvstore_compaction_test.go
@@ -15,7 +15,6 @@
package mvcc
import (
- "context"
"reflect"
"testing"
"time"
@@ -139,9 +138,9 @@ func TestCompactAllAndRestore(t *testing.T) {
if s1.Rev() != rev {
t.Errorf("rev = %v, want %v", s1.Rev(), rev)
}
- _, err = s1.Range(context.TODO(), []byte("foo"), nil, RangeOptions{})
+ _, err = s1.Range(t.Context(), []byte("foo"), nil, RangeOptions{})
if err != nil {
- t.Errorf("unexpect range error %v", err)
+ t.Errorf("unexpected range error %v", err)
}
err = s1.Close()
if err != nil {
diff --git a/server/storage/mvcc/kvstore_test.go b/server/storage/mvcc/kvstore_test.go
index 65ad4f240ec7..8c2255f2e23a 100644
--- a/server/storage/mvcc/kvstore_test.go
+++ b/server/storage/mvcc/kvstore_test.go
@@ -16,7 +16,6 @@ package mvcc
import (
"bytes"
- "context"
"crypto/rand"
"encoding/binary"
"errors"
@@ -30,8 +29,11 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -65,7 +67,7 @@ func TestStorePut(t *testing.T) {
ModRevision: 2,
Version: 1,
}
- kvb, err := kv.Marshal()
+ kvb, err := proto.Marshal(&kv)
if err != nil {
t.Fatal(err)
}
@@ -77,7 +79,7 @@ func TestStorePut(t *testing.T) {
wrev Revision
wkey []byte
- wkv mvccpb.KeyValue
+ wkv *mvccpb.KeyValue
wputrev Revision
}{
{
@@ -87,7 +89,7 @@ func TestStorePut(t *testing.T) {
Revision{Main: 2},
newTestRevBytes(Revision{Main: 2}),
- mvccpb.KeyValue{
+ &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
CreateRevision: 2,
@@ -104,7 +106,7 @@ func TestStorePut(t *testing.T) {
Revision{Main: 2},
newTestRevBytes(Revision{Main: 2}),
- mvccpb.KeyValue{
+ &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
CreateRevision: 2,
@@ -121,7 +123,7 @@ func TestStorePut(t *testing.T) {
Revision{Main: 3},
newTestRevBytes(Revision{Main: 3}),
- mvccpb.KeyValue{
+ &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
CreateRevision: 2,
@@ -145,7 +147,7 @@ func TestStorePut(t *testing.T) {
s.Put([]byte("foo"), []byte("bar"), lease.LeaseID(i+1))
- data, err := tt.wkv.Marshal()
+ data, err := proto.Marshal(tt.wkv)
if err != nil {
t.Errorf("#%d: marshal err = %v, want nil", i, err)
}
@@ -181,14 +183,14 @@ func TestStorePut(t *testing.T) {
func TestStoreRange(t *testing.T) {
lg := zaptest.NewLogger(t)
key := newTestRevBytes(Revision{Main: 2})
- kv := mvccpb.KeyValue{
+ kv := &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
CreateRevision: 1,
ModRevision: 2,
Version: 1,
}
- kvb, err := kv.Marshal()
+ kvb, err := proto.Marshal(kv)
if err != nil {
t.Fatal(err)
}
@@ -199,16 +201,16 @@ func TestStoreRange(t *testing.T) {
r rangeResp
}{
{
- indexRangeResp{[][]byte{[]byte("foo")}, []Revision{{Main: 2}}},
+ indexRangeResp{[][]byte{[]byte("foo")}, []Revision{{Main: 2}}, []Revision{{Main: 1}}, []int64{1}, 1},
rangeResp{[][]byte{key}, [][]byte{kvb}},
},
{
- indexRangeResp{[][]byte{[]byte("foo"), []byte("foo1")}, []Revision{{Main: 2}, {Main: 3}}},
+ indexRangeResp{[][]byte{[]byte("foo"), []byte("foo1")}, []Revision{{Main: 2}, {Main: 3}}, []Revision{{Main: 2}, {Main: 3}}, []int64{1, 1}, 2},
rangeResp{[][]byte{key}, [][]byte{kvb}},
},
}
- ro := RangeOptions{Limit: 1, Rev: 0, Count: false}
+ ro := RangeOptions{Limit: 1, Rev: 0, CountOnly: false}
for i, tt := range tests {
s := newFakeStore(lg)
b := s.b.(*fakeBackend)
@@ -218,11 +220,11 @@ func TestStoreRange(t *testing.T) {
b.tx.rangeRespc <- tt.r
fi.indexRangeRespc <- tt.idxr
- ret, err := s.Range(context.TODO(), []byte("foo"), []byte("goo"), ro)
+ ret, err := s.Range(t.Context(), []byte("foo"), []byte("goo"), ro)
if err != nil {
t.Errorf("#%d: err = %v, want nil", i, err)
}
- if w := []mvccpb.KeyValue{kv}; !reflect.DeepEqual(ret.KVs, w) {
+ if w := []*mvccpb.KeyValue{kv}; !cmp.Equal(ret.KVs, w, protocmp.Transform()) {
t.Errorf("#%d: kvs = %+v, want %+v", i, ret.KVs, w)
}
if ret.Rev != wrev {
@@ -261,7 +263,7 @@ func TestStoreDeleteRange(t *testing.T) {
ModRevision: 2,
Version: 1,
}
- kvb, err := kv.Marshal()
+ kvb, err := proto.Marshal(&kv)
if err != nil {
t.Fatal(err)
}
@@ -278,7 +280,7 @@ func TestStoreDeleteRange(t *testing.T) {
}{
{
Revision{Main: 2},
- indexRangeResp{[][]byte{[]byte("foo")}, []Revision{{Main: 2}}},
+ indexRangeResp{[][]byte{[]byte("foo")}, []Revision{{Main: 2}}, []Revision{{Main: 2}}, []int64{1}, 1},
rangeResp{[][]byte{key}, [][]byte{kvb}},
newTestBucketKeyBytes(newBucketKey(3, 0, true)),
@@ -301,9 +303,9 @@ func TestStoreDeleteRange(t *testing.T) {
t.Errorf("#%d: n = %d, want 1", i, n)
}
- data, err := (&mvccpb.KeyValue{
+ data, err := proto.Marshal(&mvccpb.KeyValue{
Key: []byte("foo"),
- }).Marshal()
+ })
if err != nil {
t.Errorf("#%d: marshal err = %v, want nil", i, err)
}
@@ -384,7 +386,7 @@ func TestStoreRestore(t *testing.T) {
ModRevision: 4,
Version: 1,
}
- putkvb, err := putkv.Marshal()
+ putkvb, err := proto.Marshal(&putkv)
if err != nil {
t.Fatal(err)
}
@@ -392,7 +394,7 @@ func TestStoreRestore(t *testing.T) {
delkv := mvccpb.KeyValue{
Key: []byte("foo"),
}
- delkvb, err := delkv.Marshal()
+ delkvb, err := proto.Marshal(&delkv)
if err != nil {
t.Fatal(err)
}
@@ -469,7 +471,7 @@ func TestRestoreDelete(t *testing.T) {
defer s.Close()
for i := 0; i < 20; i++ {
ks := fmt.Sprintf("foo-%d", i)
- r, err := s.Range(context.TODO(), []byte(ks), nil, RangeOptions{})
+ r, err := s.Range(t.Context(), []byte(ks), nil, RangeOptions{})
if err != nil {
t.Fatal(err)
}
@@ -519,7 +521,7 @@ func TestRestoreContinueUnfinishedCompaction(t *testing.T) {
// wait for scheduled compaction to be finished
time.Sleep(100 * time.Millisecond)
- if _, err := s.Range(context.TODO(), []byte("foo"), nil, RangeOptions{Rev: 1}); !errors.Is(err, ErrCompacted) {
+ if _, err := s.Range(t.Context(), []byte("foo"), nil, RangeOptions{Rev: 1}); !errors.Is(err, ErrCompacted) {
t.Errorf("range on compacted rev error = %v, want %v", err, ErrCompacted)
}
// check the key in backend is deleted
@@ -756,37 +758,37 @@ func TestConcurrentReadNotBlockingWrite(t *testing.T) {
// readTx2 simulates a short read request
readTx2 := s.Read(ConcurrentReadTxMode, traceutil.TODO())
- ro := RangeOptions{Limit: 1, Rev: 0, Count: false}
- ret, err := readTx2.Range(context.TODO(), []byte("foo"), nil, ro)
+ ro := RangeOptions{Limit: 1, Rev: 0, CountOnly: false}
+ ret, err := readTx2.Range(t.Context(), []byte("foo"), nil, ro)
if err != nil {
t.Fatalf("failed to range: %v", err)
}
// readTx2 should see the result of new write
- w := mvccpb.KeyValue{
+ w := &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("newBar"),
CreateRevision: 2,
ModRevision: 3,
Version: 2,
}
- if !reflect.DeepEqual(ret.KVs[0], w) {
+ if !cmp.Equal(ret.KVs[0], w, protocmp.Transform()) {
t.Fatalf("range result = %+v, want = %+v", ret.KVs[0], w)
}
readTx2.End()
- ret, err = readTx1.Range(context.TODO(), []byte("foo"), nil, ro)
+ ret, err = readTx1.Range(t.Context(), []byte("foo"), nil, ro)
if err != nil {
t.Fatalf("failed to range: %v", err)
}
// readTx1 should not see the result of new write
- w = mvccpb.KeyValue{
+ w = &mvccpb.KeyValue{
Key: []byte("foo"),
Value: []byte("bar"),
CreateRevision: 2,
ModRevision: 2,
Version: 1,
}
- if !reflect.DeepEqual(ret.KVs[0], w) {
+ if !cmp.Equal(ret.KVs[0], w, protocmp.Transform()) {
t.Fatalf("range result = %+v, want = %+v", ret.KVs[0], w)
}
readTx1.End()
@@ -841,7 +843,7 @@ func TestConcurrentReadTxAndWrite(t *testing.T) {
tx := s.Read(ConcurrentReadTxMode, traceutil.TODO())
mu.Unlock()
// get all keys in backend store, and compare with wKVs
- ret, err := tx.Range(context.TODO(), []byte("\x00000000"), []byte("\xffffffff"), RangeOptions{})
+ ret, err := tx.Range(t.Context(), []byte("\x00000000"), []byte("\xffffffff"), RangeOptions{})
tx.End()
if err != nil {
t.Errorf("failed to range keys: %v", err)
@@ -1012,8 +1014,11 @@ type indexGetResp struct {
}
type indexRangeResp struct {
- keys [][]byte
- revs []Revision
+ keys [][]byte
+ revs []Revision
+ creates []Revision
+ versions []int64
+ total int
}
type indexRangeEventsResp struct {
@@ -1028,8 +1033,8 @@ type fakeIndex struct {
indexCompactRespc chan map[Revision]struct{}
}
-func (i *fakeIndex) Revisions(key, end []byte, atRev int64, limit int) ([]Revision, int) {
- _, rev := i.Range(key, end, atRev)
+func (i *fakeIndex) Revisions(key, end []byte, atRev int64, limit int, withTotalCount bool) ([]Revision, int) {
+ _, rev, _, _, _ := i.Range(key, end, atRev, limit, withTotalCount)
if len(rev) >= limit {
rev = rev[:limit]
}
@@ -1037,7 +1042,7 @@ func (i *fakeIndex) Revisions(key, end []byte, atRev int64, limit int) ([]Revisi
}
func (i *fakeIndex) CountRevisions(key, end []byte, atRev int64) int {
- _, rev := i.Range(key, end, atRev)
+ _, rev, _, _, _ := i.Range(key, end, atRev, 0, true)
return len(rev)
}
@@ -1047,10 +1052,10 @@ func (i *fakeIndex) Get(key []byte, atRev int64) (rev, created Revision, ver int
return r.rev, r.created, r.ver, r.err
}
-func (i *fakeIndex) Range(key, end []byte, atRev int64) ([][]byte, []Revision) {
+func (i *fakeIndex) Range(key, end []byte, atRev int64, limit int, withTotalCount bool) (keys [][]byte, modifies, creates []Revision, versions []int64, total int) {
i.Recorder.Record(testutil.Action{Name: "range", Params: []any{key, end, atRev}})
r := <-i.indexRangeRespc
- return r.keys, r.revs
+ return r.keys, r.revs, r.creates, r.versions, r.total
}
func (i *fakeIndex) Put(key []byte, rev Revision) {
diff --git a/server/storage/mvcc/kvstore_txn.go b/server/storage/mvcc/kvstore_txn.go
index c44a2cb3d915..f46c79766d7d 100644
--- a/server/storage/mvcc/kvstore_txn.go
+++ b/server/storage/mvcc/kvstore_txn.go
@@ -19,6 +19,7 @@ import (
"fmt"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/pkg/v3/traceutil"
@@ -80,23 +81,39 @@ func (tr *storeTxnCommon) rangeKeys(ctx context.Context, key, end []byte, curRev
if rev < tr.s.compactMainRev {
return &RangeResult{KVs: nil, Count: -1, Rev: 0}, ErrCompacted
}
- if ro.Count {
+ if ro.CountOnly {
total := tr.s.kvindex.CountRevisions(key, end, rev)
tr.trace.Step("count revisions from in-memory index tree")
return &RangeResult{KVs: nil, Count: total, Rev: curRev}, nil
}
- revpairs, total := tr.s.kvindex.Revisions(key, end, rev, int(ro.Limit))
+
+ if ro.FastKeysOnly {
+ keys, modifies, creates, versions, total := tr.s.kvindex.Range(key, end, rev, int(ro.Limit), ro.WithTotalCount)
+ tr.trace.Step("keys only range from in-memory index tree")
+ if len(keys) == 0 {
+ return &RangeResult{KVs: nil, Count: 0, Rev: curRev}, nil
+ }
+ kvs := make([]*mvccpb.KeyValue, len(keys))
+ for i := range len(kvs) {
+ kvs[i] = &mvccpb.KeyValue{
+ Key: keys[i],
+ ModRevision: modifies[i].Main,
+ CreateRevision: creates[i].Main,
+ Version: versions[i],
+ }
+ }
+ return &RangeResult{KVs: kvs, Count: total, Rev: curRev}, nil
+ }
+
+ revpairs, total := tr.s.kvindex.Revisions(key, end, rev, int(ro.Limit), ro.WithTotalCount)
tr.trace.Step("range keys from in-memory index tree")
if len(revpairs) == 0 {
return &RangeResult{KVs: nil, Count: total, Rev: curRev}, nil
}
- limit := int(ro.Limit)
- if limit <= 0 || limit > len(revpairs) {
- limit = len(revpairs)
- }
+ cappedEntriesCount := sliceCapWithLimit(int(ro.Limit), revpairs)
- kvs := make([]mvccpb.KeyValue, limit)
+ kvs := make([]*mvccpb.KeyValue, cappedEntriesCount)
revBytes := NewRevBytes()
for i, revpair := range revpairs[:len(kvs)] {
select {
@@ -104,6 +121,7 @@ func (tr *storeTxnCommon) rangeKeys(ctx context.Context, key, end []byte, curRev
return nil, fmt.Errorf("rangeKeys: context cancelled: %w", ctx.Err())
default:
}
+
revBytes = RevToBytes(revpair, revBytes)
_, vs := tr.tx.UnsafeRange(schema.Key, revBytes, nil, 0)
if len(vs) != 1 {
@@ -120,17 +138,26 @@ func (tr *storeTxnCommon) rangeKeys(ctx context.Context, key, end []byte, curRev
zap.Int("len-values", len(vs)),
)
}
- if err := kvs[i].Unmarshal(vs[0]); err != nil {
+ kv := &mvccpb.KeyValue{}
+ if err := proto.Unmarshal(vs[0], kv); err != nil {
tr.s.lg.Fatal(
"failed to unmarshal mvccpb.KeyValue",
zap.Error(err),
)
}
+ kvs[i] = kv
}
tr.trace.Step("range keys from bolt db")
return &RangeResult{KVs: kvs, Count: total, Rev: curRev}, nil
}
+func sliceCapWithLimit[S any](limit int, s []S) int {
+ if limit <= 0 || limit > len(s) {
+ return len(s)
+ }
+ return limit
+}
+
func (tr *storeTxnRead) End() {
tr.tx.RUnlock() // RUnlock signals the end of concurrentReadTx.
tr.s.mu.RUnlock()
@@ -141,7 +168,7 @@ type storeTxnWrite struct {
tx backend.BatchTx
// beginRev is the revision where the txn begins; it will write to the next revision.
beginRev int64
- changes []mvccpb.KeyValue
+ changes []*mvccpb.KeyValue
}
func (s *store) Write(trace *traceutil.Trace) TxnWrite {
@@ -152,7 +179,7 @@ func (s *store) Write(trace *traceutil.Trace) TxnWrite {
storeTxnCommon: storeTxnCommon{s, tx, 0, 0, trace},
tx: tx,
beginRev: s.currentRev,
- changes: make([]mvccpb.KeyValue, 0, 4),
+ changes: make([]*mvccpb.KeyValue, 0, 4),
}
return newMetricsTxnWrite(tw)
}
@@ -211,7 +238,7 @@ func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) {
ibytes = RevToBytes(idxRev, ibytes)
ver = ver + 1
- kv := mvccpb.KeyValue{
+ kv := &mvccpb.KeyValue{
Key: key,
Value: value,
CreateRevision: c,
@@ -220,7 +247,7 @@ func (tw *storeTxnWrite) put(key, value []byte, leaseID lease.LeaseID) {
Lease: int64(leaseID),
}
- d, err := kv.Marshal()
+ d, err := proto.Marshal(kv)
if err != nil {
tw.storeTxnCommon.s.lg.Fatal(
"failed to marshal mvccpb.KeyValue",
@@ -268,7 +295,7 @@ func (tw *storeTxnWrite) deleteRange(key, end []byte) int64 {
if len(tw.changes) > 0 {
rrev++
}
- keys, _ := tw.s.kvindex.Range(key, end, rrev)
+ keys, _, _, _, _ := tw.s.kvindex.Range(key, end, rrev, 0, false)
if len(keys) == 0 {
return 0
}
@@ -283,9 +310,9 @@ func (tw *storeTxnWrite) delete(key []byte) {
idxRev := newBucketKey(tw.beginRev+1, int64(len(tw.changes)), true)
ibytes = BucketKeyToBytes(idxRev, ibytes)
- kv := mvccpb.KeyValue{Key: key}
+ kv := &mvccpb.KeyValue{Key: key}
- d, err := kv.Marshal()
+ d, err := proto.Marshal(kv)
if err != nil {
tw.storeTxnCommon.s.lg.Fatal(
"failed to marshal mvccpb.KeyValue",
@@ -318,4 +345,4 @@ func (tw *storeTxnWrite) delete(key []byte) {
}
}
-func (tw *storeTxnWrite) Changes() []mvccpb.KeyValue { return tw.changes }
+func (tw *storeTxnWrite) Changes() []*mvccpb.KeyValue { return tw.changes }
diff --git a/server/storage/mvcc/testutil/hash.go b/server/storage/mvcc/testutil/hash.go
index 01771bb5c542..e2c452407c90 100644
--- a/server/storage/mvcc/testutil/hash.go
+++ b/server/storage/mvcc/testutil/hash.go
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/bbolt"
"go.etcd.io/etcd/api/v3/mvccpb"
@@ -128,12 +129,12 @@ func CorruptBBolt(fpath string) error {
for k, v := c.First(); k != nil; k, v = c.Next() {
keys = append(keys, k)
var kv mvccpb.KeyValue
- if uerr := kv.Unmarshal(v); uerr != nil {
+ if uerr := proto.Unmarshal(v, &kv); uerr != nil {
return uerr
}
kv.Key[0]++
kv.Value[0]++
- v2, v2err := kv.Marshal()
+ v2, v2err := proto.Marshal(&kv)
if v2err != nil {
return v2err
}
diff --git a/server/storage/mvcc/watchable_store.go b/server/storage/mvcc/watchable_store.go
index 1a1d99fed496..8a6946f5b43b 100644
--- a/server/storage/mvcc/watchable_store.go
+++ b/server/storage/mvcc/watchable_store.go
@@ -15,11 +15,11 @@
package mvcc
import (
- "fmt"
"sync"
"time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/pkg/v3/verify"
@@ -414,7 +414,7 @@ func (s *watchableStore) syncWatchers() int {
}
// rangeEvents returns events in range [minRev, maxRev).
-func rangeEvents(lg *zap.Logger, b backend.Backend, minRev, maxRev int64, c contains) []mvccpb.Event {
+func rangeEvents(lg *zap.Logger, b backend.Backend, minRev, maxRev int64, c contains) []*mvccpb.Event {
if minRev < 0 {
lg.Warn("Unexpected negative revision range start", zap.Int64("minRev", minRev))
minRev = 0
@@ -441,10 +441,10 @@ type contains interface {
}
// kvsToEvents gets all events for the watchers from all key-value pairs
-func kvsToEvents(lg *zap.Logger, c contains, revs, vals [][]byte) (evs []mvccpb.Event) {
+func kvsToEvents(lg *zap.Logger, c contains, revs, vals [][]byte) (evs []*mvccpb.Event) {
for i, v := range vals {
- var kv mvccpb.KeyValue
- if err := kv.Unmarshal(v); err != nil {
+ kv := &mvccpb.KeyValue{}
+ if err := proto.Unmarshal(v, kv); err != nil {
lg.Panic("failed to unmarshal mvccpb.KeyValue", zap.Error(err))
}
@@ -452,20 +452,20 @@ func kvsToEvents(lg *zap.Logger, c contains, revs, vals [][]byte) (evs []mvccpb.
continue
}
- ty := mvccpb.PUT
+ ty := mvccpb.Event_PUT
if isTombstone(revs[i]) {
- ty = mvccpb.DELETE
+ ty = mvccpb.Event_DELETE
// patch in mod revision so watchers won't skip
kv.ModRevision = BytesToRev(revs[i]).Main
}
- evs = append(evs, mvccpb.Event{Kv: &kv, Type: ty})
+ evs = append(evs, &mvccpb.Event{Kv: kv, Type: ty})
}
return evs
}
// notify notifies the fact that given event at the given rev just happened to
// watchers that watch on the key of the event.
-func (s *watchableStore) notify(rev int64, evs []mvccpb.Event) {
+func (s *watchableStore) notify(rev int64, evs []*mvccpb.Event) {
victim := make(watcherBatch)
for w, eb := range newWatcherBatch(&s.synced, evs) {
if eb.revs != 1 {
@@ -575,7 +575,7 @@ func (w *watcher) send(wr WatchResponse) bool {
progressEvent := len(wr.Events) == 0
if len(w.fcs) != 0 {
- ne := make([]mvccpb.Event, 0, len(wr.Events))
+ ne := make([]*mvccpb.Event, 0, len(wr.Events))
for i := range wr.Events {
filtered := false
for _, filter := range w.fcs {
@@ -591,14 +591,19 @@ func (w *watcher) send(wr WatchResponse) bool {
wr.Events = ne
}
- verify.Verify(func() {
+ verify.Verify("Event.ModRevision is less than the w.startRev for watchID", func() (bool, map[string]any) {
if w.startRev > 0 {
for _, ev := range wr.Events {
if ev.Kv.ModRevision < w.startRev {
- panic(fmt.Sprintf("Event.ModRevision(%d) is less than the w.startRev(%d) for watchID: %d", ev.Kv.ModRevision, w.startRev, w.id))
+ return false, map[string]any{
+ "Event.ModRevision": ev.Kv.ModRevision,
+ "w.startRev": w.startRev,
+ "watchID": w.id,
+ }
}
}
}
+ return true, nil
})
// if all events are filtered out, we should send nothing.
diff --git a/server/storage/mvcc/watchable_store_bench_test.go b/server/storage/mvcc/watchable_store_bench_test.go
index c8990576b30a..c1da715eee96 100644
--- a/server/storage/mvcc/watchable_store_bench_test.go
+++ b/server/storage/mvcc/watchable_store_bench_test.go
@@ -94,7 +94,7 @@ func benchmarkWatchableStoreWatchPut(b *testing.B, synced bool) {
defer w.Close()
watchIDs := make([]WatchID, b.N)
for i := range watchIDs {
- watchIDs[i], _ = w.Watch(0, k, nil, rev)
+ watchIDs[i], _ = w.Watch(b.Context(), 0, k, nil, rev)
}
b.ResetTimer()
@@ -144,7 +144,7 @@ func BenchmarkWatchableStoreUnsyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// non-0 value to keep watchers in unsynced
- watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
+ watchIDs[i], _ = w.Watch(b.Context(), 0, testKey, nil, 1)
}
// random-cancel N watchers to make it not biased towards
@@ -182,7 +182,7 @@ func BenchmarkWatchableStoreSyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// 0 for startRev to keep watchers in synced
- watchIDs[i], _ = w.Watch(0, testKey, nil, 0)
+ watchIDs[i], _ = w.Watch(b.Context(), 0, testKey, nil, 0)
}
// randomly cancel watchers to make it not biased towards
diff --git a/server/storage/mvcc/watchable_store_test.go b/server/storage/mvcc/watchable_store_test.go
index e88f5db9e9c5..70cd3d7b7523 100644
--- a/server/storage/mvcc/watchable_store_test.go
+++ b/server/storage/mvcc/watchable_store_test.go
@@ -16,17 +16,18 @@ package mvcc
import (
"fmt"
- "reflect"
"strings"
"sync"
"testing"
"time"
"github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/pkg/v3/traceutil"
@@ -46,7 +47,7 @@ func TestWatch(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- w.Watch(0, testKey, nil, 0)
+ w.Watch(t.Context(), 0, testKey, nil, 0)
if !s.(*watchableStore).synced.contains(string(testKey)) {
// the key must have had an entry in synced
t.Errorf("existence = false, want true")
@@ -65,13 +66,13 @@ func TestNewWatcherCancel(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- wt, _ := w.Watch(0, testKey, nil, 0)
+ wt, _ := w.Watch(t.Context(), 0, testKey, nil, 0)
if err := w.Cancel(wt); err != nil {
t.Error(err)
}
if s.(*watchableStore).synced.contains(string(testKey)) {
- // the key shoud have been deleted
+ // the key should have been deleted
t.Errorf("existence = true, want false")
}
}
@@ -107,7 +108,7 @@ etcd_debugging_mvcc_watcher_total %d
w := s.NewWatchStream()
defer w.Close()
- wt, _ := w.Watch(0, testKey, nil, 0)
+ wt, _ := w.Watch(t.Context(), 0, testKey, nil, 0)
// after creating watch, the gauge state should have increased
expectWatchGauge(initialGaugeState + 1)
@@ -152,7 +153,7 @@ etcd_debugging_mvcc_watcher_total %d
w := s.NewWatchStream()
defer w.Close()
- wt, _ := w.Watch(0, testKey, nil, rev-1)
+ wt, _ := w.Watch(t.Context(), 0, testKey, nil, rev-1)
// wait for the watcher to be marked as compacted
select {
@@ -206,7 +207,7 @@ etcd_debugging_mvcc_watcher_total %d
w := s.NewWatchStream()
- wt, _ := w.Watch(0, testKey, nil, rev-1)
+ wt, _ := w.Watch(t.Context(), 0, testKey, nil, rev-1)
// wait for the watcher to be marked as compacted
select {
@@ -274,7 +275,7 @@ func TestCancelUnsynced(t *testing.T) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// use 1 to keep watchers in unsynced
- watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
+ watchIDs[i], _ = w.Watch(t.Context(), 0, testKey, nil, 1)
}
for _, idx := range watchIDs {
@@ -307,7 +308,7 @@ func TestSyncWatchers(t *testing.T) {
defer w.Close()
watcherN := 100
for i := 0; i < watcherN; i++ {
- _, err := w.Watch(0, testKey, nil, 1)
+ _, err := w.Watch(t.Context(), 0, testKey, nil, 1)
require.NoError(t, err)
}
@@ -321,9 +322,9 @@ func TestSyncWatchers(t *testing.T) {
for i := 0; i < watcherN; i++ {
events := (<-w.(*watchStream).ch).Events
assert.Len(t, events, 1)
- assert.Equal(t, []mvccpb.Event{
+ want := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: testKey,
CreateRevision: 2,
@@ -332,7 +333,10 @@ func TestSyncWatchers(t *testing.T) {
Value: testValue,
},
},
- }, events)
+ }
+ if diff := cmp.Diff(want, events, protocmp.Transform()); diff != "" {
+ t.Fatalf("unexpected events (-want +got):\n%s", diff)
+ }
}
}
@@ -352,9 +356,9 @@ func TestRangeEvents(t *testing.T) {
s.Put(foo3, value, lease.NoLease)
s.DeleteRange(foo1, foo3) // Deletes "foo1" and "foo2" generating 2 events
- expectEvents := []mvccpb.Event{
+ expectEvents := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: foo1,
CreateRevision: 2,
@@ -364,7 +368,7 @@ func TestRangeEvents(t *testing.T) {
},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: foo2,
CreateRevision: 3,
@@ -374,7 +378,7 @@ func TestRangeEvents(t *testing.T) {
},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{
Key: foo3,
CreateRevision: 4,
@@ -384,14 +388,14 @@ func TestRangeEvents(t *testing.T) {
},
},
{
- Type: mvccpb.DELETE,
+ Type: mvccpb.Event_DELETE,
Kv: &mvccpb.KeyValue{
Key: foo1,
ModRevision: 5,
},
},
{
- Type: mvccpb.DELETE,
+ Type: mvccpb.Event_DELETE,
Kv: &mvccpb.KeyValue{
Key: foo2,
ModRevision: 5,
@@ -402,7 +406,7 @@ func TestRangeEvents(t *testing.T) {
tcs := []struct {
minRev int64
maxRev int64
- expectEvents []mvccpb.Event
+ expectEvents []*mvccpb.Event
}{
// maxRev, top to bottom
{minRev: -1, maxRev: 6, expectEvents: expectEvents[0:5]},
@@ -432,7 +436,9 @@ func TestRangeEvents(t *testing.T) {
}
for i, tc := range tcs {
t.Run(fmt.Sprintf("%d rangeEvents(%d, %d)", i, tc.minRev, tc.maxRev), func(t *testing.T) {
- assert.ElementsMatch(t, tc.expectEvents, rangeEvents(lg, b, tc.minRev, tc.maxRev, fakeContains{}))
+ if diff := cmp.Diff(tc.expectEvents, rangeEvents(lg, b, tc.minRev, tc.maxRev, fakeContains{}), protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" {
+ t.Fatalf("unexpected events (-want +got):\n%s", diff)
+ }
})
}
}
@@ -465,7 +471,7 @@ func TestWatchCompacted(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- wt, _ := w.Watch(0, testKey, nil, compactRev-1)
+ wt, _ := w.Watch(t.Context(), 0, testKey, nil, compactRev-1)
select {
case resp := <-w.Chan():
if resp.WatchID != wt {
@@ -511,7 +517,7 @@ func TestWatchNoEventLossOnCompact(t *testing.T) {
2: 6, // create unsyncd watchers with compactRev < startRev < currentRev
}
for id, startRev := range watchers {
- _, err := w.Watch(id, testKey, nil, startRev)
+ _, err := w.Watch(t.Context(), id, testKey, nil, startRev)
require.NoError(t, err)
}
// fill up w.Chan() with 1 buf via 2 compacted watch response
@@ -550,7 +556,7 @@ func TestWatchFutureRev(t *testing.T) {
defer w.Close()
wrev := int64(10)
- w.Watch(0, testKey, nil, wrev)
+ w.Watch(t.Context(), 0, testKey, nil, wrev)
for i := 0; i < 10; i++ {
rev := s.Put(testKey, testValue, lease.NoLease)
@@ -604,43 +610,43 @@ func testWatchRestore(t *testing.T, delayBeforeRestore, delayAfterRestore time.D
tcs := []struct {
name string
startRevision int64
- wantEvents []mvccpb.Event
+ wantEvents []*mvccpb.Event
}{
{
name: "zero revision",
startRevision: 0,
- wantEvents: []mvccpb.Event{
- {Type: mvccpb.PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
+ wantEvents: []*mvccpb.Event{
+ {Type: mvccpb.Event_PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
},
},
{
- name: "revsion before first write",
+ name: "revision before first write",
startRevision: 1,
- wantEvents: []mvccpb.Event{
- {Type: mvccpb.PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
+ wantEvents: []*mvccpb.Event{
+ {Type: mvccpb.Event_PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
},
},
{
name: "revision of first write",
startRevision: 2,
- wantEvents: []mvccpb.Event{
- {Type: mvccpb.PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
+ wantEvents: []*mvccpb.Event{
+ {Type: mvccpb.Event_PUT, Kv: &mvccpb.KeyValue{Key: testKey, Value: testValue, CreateRevision: 2, ModRevision: 2, Version: 1}},
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
},
},
{
name: "current revision",
startRevision: 3,
- wantEvents: []mvccpb.Event{
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
+ wantEvents: []*mvccpb.Event{
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: testKey, ModRevision: 3}},
},
},
{
name: "future revision",
startRevision: 4,
- wantEvents: []mvccpb.Event{},
+ wantEvents: []*mvccpb.Event{},
},
}
watchers := []WatchStream{}
@@ -648,7 +654,7 @@ func testWatchRestore(t *testing.T, delayBeforeRestore, delayAfterRestore time.D
w := s.NewWatchStream()
defer w.Close()
watchers = append(watchers, w)
- w.Watch(WatchID(i+1), testKey, nil, tc.startRevision)
+ w.Watch(t.Context(), WatchID(i+1), testKey, nil, tc.startRevision)
}
s.Put(testKey, testValue, lease.NoLease)
@@ -660,15 +666,15 @@ func testWatchRestore(t *testing.T, delayBeforeRestore, delayAfterRestore time.D
for i, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
events := readEventsForSecond(t, watchers[i].Chan())
- if diff := cmp.Diff(tc.wantEvents, events); diff != "" {
+ if diff := cmp.Diff(tc.wantEvents, events, protocmp.Transform()); diff != "" {
t.Errorf("unexpected events (-want +got):\n%s", diff)
}
})
}
}
-func readEventsForSecond(t *testing.T, ws <-chan WatchResponse) []mvccpb.Event {
- events := []mvccpb.Event{}
+func readEventsForSecond(t *testing.T, ws <-chan WatchResponse) []*mvccpb.Event {
+ events := []*mvccpb.Event{}
deadline := time.After(time.Second)
for {
select {
@@ -740,7 +746,7 @@ func TestWatchBatchUnsynced(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- w.Watch(0, v, nil, 1)
+ w.Watch(t.Context(), 0, v, nil, 1)
var revisionBatches [][]int64
eventCount := 0
for eventCount < tc.revisions*tc.eventsPerRevision {
@@ -770,32 +776,32 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
ws := []*watcher{{key: k0}, {key: k1}, {key: k2}}
- evs := []mvccpb.Event{
+ evs := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: k0, Value: v0},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: k1, Value: v1},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: k2, Value: v2},
},
}
tests := []struct {
sync []*watcher
- evs []mvccpb.Event
+ evs []*mvccpb.Event
- wwe map[*watcher][]mvccpb.Event
+ wwe map[*watcher][]*mvccpb.Event
}{
// no watcher in sync, some events should return empty wwe
{
nil,
evs,
- map[*watcher][]mvccpb.Event{},
+ map[*watcher][]*mvccpb.Event{},
},
// one watcher in sync, one event that does not match the key of that
@@ -803,7 +809,7 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
{
[]*watcher{ws[2]},
evs[:1],
- map[*watcher][]mvccpb.Event{},
+ map[*watcher][]*mvccpb.Event{},
},
// one watcher in sync, one event that matches the key of that
@@ -811,7 +817,7 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
{
[]*watcher{ws[1]},
evs[1:2],
- map[*watcher][]mvccpb.Event{
+ map[*watcher][]*mvccpb.Event{
ws[1]: evs[1:2],
},
},
@@ -822,7 +828,7 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
{
[]*watcher{ws[0], ws[2]},
evs[2:],
- map[*watcher][]mvccpb.Event{
+ map[*watcher][]*mvccpb.Event{
ws[2]: evs[2:],
},
},
@@ -832,7 +838,7 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
{
[]*watcher{ws[0], ws[1]},
evs[:2],
- map[*watcher][]mvccpb.Event{
+ map[*watcher][]*mvccpb.Event{
ws[0]: evs[:1],
ws[1]: evs[1:2],
},
@@ -854,8 +860,8 @@ func TestNewMapwatcherToEventMap(t *testing.T) {
if len(eb.evs) != len(tt.wwe[w]) {
t.Errorf("#%d: len(eb.evs) got = %d, want = %d", i, len(eb.evs), len(tt.wwe[w]))
}
- if !reflect.DeepEqual(eb.evs, tt.wwe[w]) {
- t.Errorf("#%d: reflect.DeepEqual events got = %v, want = true", i, false)
+ if diff := cmp.Diff(tt.wwe[w], eb.evs, protocmp.Transform()); diff != "" {
+ t.Errorf("#%d: events mismatch (-want +got):\n%s", i, diff)
}
}
}
@@ -885,7 +891,7 @@ func TestWatchVictims(t *testing.T) {
for i := 0; i < numWatches; i++ {
go func() {
w := s.NewWatchStream()
- w.Watch(0, testKey, nil, 1)
+ w.Watch(t.Context(), 0, testKey, nil, 1)
defer func() {
w.Close()
wg.Done()
@@ -957,7 +963,7 @@ func TestStressWatchCancelClose(t *testing.T) {
w := s.NewWatchStream()
ids := make([]WatchID, 10)
for i := range ids {
- ids[i], _ = w.Watch(0, testKey, nil, 0)
+ ids[i], _ = w.Watch(t.Context(), 0, testKey, nil, 0)
}
<-readyc
wg.Add(1 + len(ids)/2)
diff --git a/server/storage/mvcc/watchable_store_txn.go b/server/storage/mvcc/watchable_store_txn.go
index b70d8ceca47b..42eb6ca06120 100644
--- a/server/storage/mvcc/watchable_store_txn.go
+++ b/server/storage/mvcc/watchable_store_txn.go
@@ -27,14 +27,14 @@ func (tw *watchableStoreTxnWrite) End() {
}
rev := tw.Rev() + 1
- evs := make([]mvccpb.Event, len(changes))
+ evs := make([]*mvccpb.Event, len(changes))
for i, change := range changes {
- evs[i].Kv = &changes[i]
+ evs[i] = &mvccpb.Event{Kv: changes[i]}
if change.CreateRevision == 0 {
- evs[i].Type = mvccpb.DELETE
+ evs[i].Type = mvccpb.Event_DELETE
evs[i].Kv.ModRevision = rev
} else {
- evs[i].Type = mvccpb.PUT
+ evs[i].Type = mvccpb.Event_PUT
}
}
diff --git a/server/storage/mvcc/watcher.go b/server/storage/mvcc/watcher.go
index c67c21d61397..1d497ccb825c 100644
--- a/server/storage/mvcc/watcher.go
+++ b/server/storage/mvcc/watcher.go
@@ -16,9 +16,12 @@ package mvcc
import (
"bytes"
+ "context"
"errors"
"sync"
+ "go.opentelemetry.io/otel/trace"
+
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
@@ -32,7 +35,7 @@ var (
type WatchID int64
// FilterFunc returns true if the given event should be filtered out.
-type FilterFunc func(e mvccpb.Event) bool
+type FilterFunc func(e *mvccpb.Event) bool
type WatchStream interface {
// Watch creates a watcher. The watcher watches the events happening or
@@ -45,7 +48,7 @@ type WatchStream interface {
// in events that are sent to the created watcher through stream channel.
// The watch ID is used when it's not equal to AutoWatchID. Otherwise,
// an auto-generated watch ID is returned.
- Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error)
+ Watch(ctx context.Context, id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error)
// Chan returns a chan. All watch response will be sent to the returned chan.
Chan() <-chan WatchResponse
@@ -81,7 +84,7 @@ type WatchResponse struct {
WatchID WatchID
// Events contains all the events that needs to send.
- Events []mvccpb.Event
+ Events []*mvccpb.Event
// Revision is the revision of the KV when the watchResponse is created.
// For a normal response, the revision should be the same as the last
@@ -109,7 +112,7 @@ type watchStream struct {
}
// Watch creates a new watcher in the stream and returns its WatchID.
-func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
+func (ws *watchStream) Watch(ctx context.Context, id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
// prevent wrong range where key >= end lexicographically
// watch request with 'WithFromKey' has empty-byte range end
if len(end) != 0 && bytes.Compare(key, end) != -1 {
@@ -134,7 +137,11 @@ func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ..
w, c := ws.watchable.watch(key, end, startRev, id, ws.ch, fcs...)
- ws.cancels[id] = c
+ span := trace.SpanFromContext(ctx)
+ ws.cancels[id] = func() {
+ defer span.End()
+ c()
+ }
ws.watchers[id] = w
return id, nil
}
diff --git a/server/storage/mvcc/watcher_bench_test.go b/server/storage/mvcc/watcher_bench_test.go
index 3d0dccea3421..be316fa8390b 100644
--- a/server/storage/mvcc/watcher_bench_test.go
+++ b/server/storage/mvcc/watcher_bench_test.go
@@ -36,6 +36,6 @@ func BenchmarkKVWatcherMemoryUsage(b *testing.B) {
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
- w.Watch(0, []byte(fmt.Sprint("foo", i)), nil, 0)
+ w.Watch(b.Context(), 0, []byte(fmt.Sprint("foo", i)), nil, 0)
}
}
diff --git a/server/storage/mvcc/watcher_group.go b/server/storage/mvcc/watcher_group.go
index c9db0e2bd9b0..4390293496f5 100644
--- a/server/storage/mvcc/watcher_group.go
+++ b/server/storage/mvcc/watcher_group.go
@@ -29,14 +29,14 @@ var watchBatchMaxRevs = 1000
type eventBatch struct {
// evs is a batch of revision-ordered events
- evs []mvccpb.Event
+ evs []*mvccpb.Event
// revs is the minimum unique revisions observed for this batch
revs int
// moreRev is first revision with more events following this batch
moreRev int64
}
-func (eb *eventBatch) add(ev mvccpb.Event) {
+func (eb *eventBatch) add(ev *mvccpb.Event) {
if eb.revs > watchBatchMaxRevs {
// maxed out batch size
return
@@ -65,7 +65,7 @@ func (eb *eventBatch) add(ev mvccpb.Event) {
type watcherBatch map[*watcher]*eventBatch
-func (wb watcherBatch) add(w *watcher, ev mvccpb.Event) {
+func (wb watcherBatch) add(w *watcher, ev *mvccpb.Event) {
eb := wb[w]
if eb == nil {
eb = &eventBatch{}
@@ -76,7 +76,7 @@ func (wb watcherBatch) add(w *watcher, ev mvccpb.Event) {
// newWatcherBatch maps watchers to their matched events. It enables quick
// events look up by watcher.
-func newWatcherBatch(wg *watcherGroup, evs []mvccpb.Event) watcherBatch {
+func newWatcherBatch(wg *watcherGroup, evs []*mvccpb.Event) watcherBatch {
if len(wg.watchers) == 0 {
return nil
}
@@ -86,7 +86,11 @@ func newWatcherBatch(wg *watcherGroup, evs []mvccpb.Event) watcherBatch {
for w := range wg.watcherSetByKey(string(ev.Kv.Key)) {
if ev.Kv.ModRevision >= w.minRev {
// don't double notify
- wb.add(w, ev)
+ wb.add(w, &mvccpb.Event{
+ Type: ev.Type,
+ Kv: ev.Kv,
+ PrevKv: ev.PrevKv,
+ })
}
}
}
diff --git a/server/storage/mvcc/watcher_test.go b/server/storage/mvcc/watcher_test.go
index d127652e0e16..32f6bfe92dc8 100644
--- a/server/storage/mvcc/watcher_test.go
+++ b/server/storage/mvcc/watcher_test.go
@@ -23,7 +23,9 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
@@ -44,7 +46,7 @@ func TestWatcherWatchID(t *testing.T) {
idm := make(map[WatchID]struct{})
for i := 0; i < 10; i++ {
- id, _ := w.Watch(0, []byte("foo"), nil, 0)
+ id, _ := w.Watch(t.Context(), 0, []byte("foo"), nil, 0)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@@ -66,7 +68,7 @@ func TestWatcherWatchID(t *testing.T) {
// unsynced watchers
for i := 10; i < 20; i++ {
- id, _ := w.Watch(0, []byte("foo2"), nil, 1)
+ id, _ := w.Watch(t.Context(), 0, []byte("foo2"), nil, 1)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@@ -107,10 +109,10 @@ func TestWatcherRequestsCustomID(t *testing.T) {
}
for i, tcase := range tt {
- id, err := w.Watch(tcase.givenID, []byte("foo"), nil, 0)
+ id, err := w.Watch(t.Context(), tcase.givenID, []byte("foo"), nil, 0)
if tcase.expectedErr != nil || err != nil {
if !errors.Is(err, tcase.expectedErr) {
- t.Errorf("expected get error %q in test case %q, got %q", tcase.expectedErr, i, err)
+ t.Errorf("expected get error %q in test case %d, got %q", tcase.expectedErr, i, err)
}
} else if tcase.expectedID != id {
t.Errorf("expected to create ID %d, got %d in test case %d", tcase.expectedID, id, i)
@@ -134,7 +136,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")
for i := 0; i < 10; i++ {
- id, _ := w.Watch(0, keyWatch, keyEnd, 0)
+ id, _ := w.Watch(t.Context(), 0, keyWatch, keyEnd, 0)
if _, ok := idm[id]; ok {
t.Errorf("#%d: unexpected duplicated id %x", i, id)
}
@@ -166,7 +168,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
// unsynced watchers
for i := 10; i < 15; i++ {
- id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
+ id, _ := w.Watch(t.Context(), 0, keyWatch1, keyEnd1, 1)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@@ -192,6 +194,59 @@ func TestWatcherWatchPrefix(t *testing.T) {
}
}
+func TestWatchResponseEventsNotSharedAcrossWatchers(t *testing.T) {
+ b, _ := betesting.NewDefaultTmpBackend(t)
+ s := New(zaptest.NewLogger(t), b, &lease.FakeLessor{}, StoreConfig{})
+ defer cleanup(s, b)
+
+ w := s.NewWatchStream()
+ defer w.Close()
+
+ key := []byte("foo")
+ value := []byte("bar")
+ id1, err := w.Watch(t.Context(), 0, key, nil, 0)
+ if err != nil {
+ t.Fatalf("failed to create first watcher: %v", err)
+ }
+ id2, err := w.Watch(t.Context(), 0, key, nil, 0)
+ if err != nil {
+ t.Fatalf("failed to create second watcher: %v", err)
+ }
+
+ s.Put(key, value, lease.NoLease)
+ respByID := collectWatchResponsesForWatchers(t, w.Chan(), id1, id2)
+ resp1 := respByID[id1]
+ resp2 := respByID[id2]
+
+ if len(resp1.Events) != 1 || len(resp2.Events) != 1 {
+ t.Fatalf("unexpected event count: first response has %d events, second response has %d events", len(resp1.Events), len(resp2.Events))
+ }
+ if resp1.Events[0] == resp2.Events[0] {
+ t.Fatalf("watch responses share the same event pointer")
+ }
+}
+
+func collectWatchResponsesForWatchers(t *testing.T, ch <-chan WatchResponse, watcherIDs ...WatchID) map[WatchID]WatchResponse {
+ t.Helper()
+ target := make(map[WatchID]struct{}, len(watcherIDs))
+ for _, id := range watcherIDs {
+ target[id] = struct{}{}
+ }
+
+ respByID := make(map[WatchID]WatchResponse, len(watcherIDs))
+ for len(respByID) < len(watcherIDs) {
+ select {
+ case resp := <-ch:
+ if _, ok := target[resp.WatchID]; ok {
+ respByID[resp.WatchID] = resp
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for watch responses; got %d, want %d", len(respByID), len(watcherIDs))
+ }
+ }
+ return respByID
+}
+
// TestWatcherWatchWrongRange ensures that watcher with wrong 'end' range
// does not create watcher, which panics when canceling in range tree.
func TestWatcherWatchWrongRange(t *testing.T) {
@@ -202,14 +257,14 @@ func TestWatcherWatchWrongRange(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); !errors.Is(err, ErrEmptyWatcherRange) {
+ if _, err := w.Watch(t.Context(), 0, []byte("foa"), []byte("foa"), 1); !errors.Is(err, ErrEmptyWatcherRange) {
t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
}
- if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); !errors.Is(err, ErrEmptyWatcherRange) {
+ if _, err := w.Watch(t.Context(), 0, []byte("fob"), []byte("foa"), 1); !errors.Is(err, ErrEmptyWatcherRange) {
t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
}
// watch request with 'WithFromKey' has empty-byte range end
- if id, _ := w.Watch(0, []byte("foo"), []byte{}, 1); id != 0 {
+ if id, _ := w.Watch(t.Context(), 0, []byte("foo"), []byte{}, 1); id != 0 {
t.Fatalf("\x00 is range given; id expected 0, got %d", id)
}
}
@@ -232,20 +287,20 @@ func TestWatchDeleteRange(t *testing.T) {
w := s.NewWatchStream()
from, to := testKeyPrefix, []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99))
- w.Watch(0, from, to, 0)
+ w.Watch(t.Context(), 0, from, to, 0)
s.DeleteRange(from, to)
- we := []mvccpb.Event{
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_0"), ModRevision: 5}},
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_1"), ModRevision: 5}},
- {Type: mvccpb.DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_2"), ModRevision: 5}},
+ we := []*mvccpb.Event{
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_0"), ModRevision: 5}},
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_1"), ModRevision: 5}},
+ {Type: mvccpb.Event_DELETE, Kv: &mvccpb.KeyValue{Key: []byte("foo_2"), ModRevision: 5}},
}
select {
case r := <-w.Chan():
- if !reflect.DeepEqual(r.Events, we) {
- t.Errorf("event = %v, want %v", r.Events, we)
+ if diff := cmp.Diff(we, r.Events, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected events (-want +got):\n%s", diff)
}
case <-time.After(10 * time.Second):
t.Fatal("failed to receive event after 10 seconds!")
@@ -262,7 +317,7 @@ func TestWatchStreamCancelWatcherByID(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- id, _ := w.Watch(0, []byte("foo"), nil, 0)
+ id, _ := w.Watch(t.Context(), 0, []byte("foo"), nil, 0)
tests := []struct {
cancelID WatchID
@@ -350,7 +405,7 @@ func TestWatcherRequestProgress(t *testing.T) {
w := s.NewWatchStream()
- id, _ := w.Watch(0, notTestKey, nil, tc.startRev)
+ id, _ := w.Watch(t.Context(), 0, notTestKey, nil, tc.startRev)
w.RequestProgress(id)
asssertProgressSent(t, w, id, tc.expectProgressBeforeSync)
s.syncWatchers()
@@ -394,7 +449,7 @@ func TestWatcherRequestProgressAll(t *testing.T) {
// at least one Watch for progress notifications to get
// generated.
w := s.NewWatchStream()
- w.Watch(0, notTestKey, nil, 1)
+ w.Watch(t.Context(), 0, notTestKey, nil, 1)
w.RequestProgressAll()
select {
@@ -425,11 +480,11 @@ func TestWatcherWatchWithFilter(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
- filterPut := func(e mvccpb.Event) bool {
- return e.Type == mvccpb.PUT
+ filterPut := func(e *mvccpb.Event) bool {
+ return e.Type == mvccpb.Event_PUT
}
- w.Watch(0, []byte("foo"), nil, 0, filterPut)
+ w.Watch(t.Context(), 0, []byte("foo"), nil, 0, filterPut)
done := make(chan struct{}, 1)
go func() {
diff --git a/server/storage/schema/alarm.go b/server/storage/schema/alarm.go
index 929fbd4cebdb..707c46dc87d9 100644
--- a/server/storage/schema/alarm.go
+++ b/server/storage/schema/alarm.go
@@ -16,6 +16,7 @@ package schema
import (
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -56,7 +57,7 @@ func (s *alarmBackend) MustPutAlarm(alarm *etcdserverpb.AlarmMember) {
}
func (s *alarmBackend) mustUnsafePutAlarm(tx backend.UnsafeWriter, alarm *etcdserverpb.AlarmMember) {
- v, err := alarm.Marshal()
+ v, err := proto.Marshal(alarm)
if err != nil {
s.lg.Panic("failed to marshal alarm member", zap.Error(err))
}
@@ -72,7 +73,7 @@ func (s *alarmBackend) MustDeleteAlarm(alarm *etcdserverpb.AlarmMember) {
}
func (s *alarmBackend) mustUnsafeDeleteAlarm(tx backend.UnsafeWriter, alarm *etcdserverpb.AlarmMember) {
- v, err := alarm.Marshal()
+ v, err := proto.Marshal(alarm)
if err != nil {
s.lg.Panic("failed to marshal alarm member", zap.Error(err))
}
@@ -91,7 +92,7 @@ func (s *alarmBackend) unsafeGetAllAlarms(tx backend.UnsafeReader) ([]*etcdserve
var ms []*etcdserverpb.AlarmMember
err := tx.UnsafeForEach(Alarm, func(k, v []byte) error {
var m etcdserverpb.AlarmMember
- if err := m.Unmarshal(k); err != nil {
+ if err := proto.Unmarshal(k, &m); err != nil {
return err
}
ms = append(ms, &m)
diff --git a/server/storage/schema/auth_roles.go b/server/storage/schema/auth_roles.go
index 6161a0885a91..379f719b1a5d 100644
--- a/server/storage/schema/auth_roles.go
+++ b/server/storage/schema/auth_roles.go
@@ -16,6 +16,7 @@ package schema
import (
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/authpb"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -48,7 +49,7 @@ func (atx *authBatchTx) UnsafeGetAllRoles() []*authpb.Role {
}
func (atx *authBatchTx) UnsafePutRole(role *authpb.Role) {
- b, err := role.Marshal()
+ b, err := proto.Marshal(role)
if err != nil {
atx.lg.Panic(
"failed to marshal 'authpb.Role'",
@@ -75,7 +76,7 @@ func unsafeGetRole(lg *zap.Logger, tx backend.UnsafeReader, roleName string) *au
}
role := &authpb.Role{}
- err := role.Unmarshal(vs[0])
+ err := proto.Unmarshal(vs[0], role)
if err != nil {
lg.Panic("failed to unmarshal 'authpb.Role'", zap.Error(err))
}
@@ -95,7 +96,7 @@ func unsafeGetAllRoles(lg *zap.Logger, tx backend.UnsafeReader) []*authpb.Role {
roles := make([]*authpb.Role, len(vs))
for i := range vs {
role := &authpb.Role{}
- err := role.Unmarshal(vs[i])
+ err := proto.Unmarshal(vs[i], role)
if err != nil {
lg.Panic("failed to unmarshal 'authpb.Role'", zap.Error(err))
}
diff --git a/server/storage/schema/auth_roles_test.go b/server/storage/schema/auth_roles_test.go
index 7f90663d854d..9f900397a7b6 100644
--- a/server/storage/schema/auth_roles_test.go
+++ b/server/storage/schema/auth_roles_test.go
@@ -18,8 +18,9 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/assert"
+ "github.com/google/go-cmp/cmp"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/authpb"
"go.etcd.io/etcd/server/v3/auth"
@@ -45,7 +46,7 @@ func TestGetAllRoles(t *testing.T) {
Name: []byte("readKey"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte("key"),
RangeEnd: []byte("end"),
},
@@ -57,7 +58,7 @@ func TestGetAllRoles(t *testing.T) {
Name: []byte("readKey"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte("key"),
RangeEnd: []byte("end"),
},
@@ -85,7 +86,7 @@ func TestGetAllRoles(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
},
},
})
@@ -96,13 +97,13 @@ func TestGetAllRoles(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READWRITE,
+ PermType: authpb.Permission_READWRITE,
},
},
})
},
want: []*authpb.Role{
- {Name: []byte("role1"), KeyPermission: []*authpb.Permission{{PermType: authpb.READWRITE}}},
+ {Name: []byte("role1"), KeyPermission: []*authpb.Permission{{PermType: authpb.Permission_READWRITE}}},
{Name: []byte("role2")},
},
},
@@ -127,7 +128,9 @@ func TestGetAllRoles(t *testing.T) {
abe2 := NewAuthBackend(lg, be2)
users := abe2.GetAllRoles()
- assert.Equal(t, tc.want, users)
+ if diff := cmp.Diff(tc.want, users, protocmp.Transform()); diff != "" {
+ t.Fatalf("roles mismatch (-want +got):\n%s", diff)
+ }
})
}
}
@@ -150,7 +153,7 @@ func TestGetRole(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte("key"),
RangeEnd: []byte("end"),
},
@@ -161,7 +164,7 @@ func TestGetRole(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte("key"),
RangeEnd: []byte("end"),
},
@@ -185,7 +188,7 @@ func TestGetRole(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
},
},
})
@@ -193,14 +196,14 @@ func TestGetRole(t *testing.T) {
Name: []byte("role1"),
KeyPermission: []*authpb.Permission{
{
- PermType: authpb.READWRITE,
+ PermType: authpb.Permission_READWRITE,
},
},
})
},
want: &authpb.Role{
Name: []byte("role1"),
- KeyPermission: []*authpb.Permission{{PermType: authpb.READWRITE}},
+ KeyPermission: []*authpb.Permission{{PermType: authpb.Permission_READWRITE}},
},
},
}
@@ -224,7 +227,9 @@ func TestGetRole(t *testing.T) {
abe2 := NewAuthBackend(lg, be2)
users := abe2.GetRole("role1")
- assert.Equal(t, tc.want, users)
+ if diff := cmp.Diff(tc.want, users, protocmp.Transform()); diff != "" {
+ t.Fatalf("roles mismatch (-want +got):\n%s", diff)
+ }
})
}
}
diff --git a/server/storage/schema/auth_users.go b/server/storage/schema/auth_users.go
index c21fa7c16d8a..3559ec60fed6 100644
--- a/server/storage/schema/auth_users.go
+++ b/server/storage/schema/auth_users.go
@@ -16,6 +16,7 @@ package schema
import (
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/authpb"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -37,7 +38,7 @@ func (atx *authBatchTx) UnsafeGetAllUsers() []*authpb.User {
}
func (atx *authBatchTx) UnsafePutUser(user *authpb.User) {
- b, err := user.Marshal()
+ b, err := proto.Marshal(user)
if err != nil {
atx.lg.Panic("failed to unmarshal 'authpb.User'", zap.Error(err))
}
@@ -59,7 +60,7 @@ func unsafeGetUser(lg *zap.Logger, tx backend.UnsafeReader, username string) *au
}
user := &authpb.User{}
- err := user.Unmarshal(vs[0])
+ err := proto.Unmarshal(vs[0], user)
if err != nil {
lg.Panic(
"failed to unmarshal 'authpb.User'",
@@ -98,7 +99,7 @@ func unsafeGetAllUsers(lg *zap.Logger, tx backend.UnsafeReader) []*authpb.User {
users := make([]*authpb.User, len(vs))
for i := range vs {
user := &authpb.User{}
- err := user.Unmarshal(vs[i])
+ err := proto.Unmarshal(vs[i], user)
if err != nil {
lg.Panic("failed to unmarshal 'authpb.User'", zap.Error(err))
}
diff --git a/server/storage/schema/auth_users_test.go b/server/storage/schema/auth_users_test.go
index f109697d0aff..e19ee42eca59 100644
--- a/server/storage/schema/auth_users_test.go
+++ b/server/storage/schema/auth_users_test.go
@@ -18,8 +18,9 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/assert"
+ "github.com/google/go-cmp/cmp"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/api/v3/authpb"
"go.etcd.io/etcd/server/v3/auth"
@@ -115,7 +116,9 @@ func TestGetAllUsers(t *testing.T) {
abe2 := NewAuthBackend(lg, be2)
users := abe2.ReadTx().UnsafeGetAllUsers()
- assert.Equal(t, tc.want, users)
+ if diff := cmp.Diff(tc.want, users, protocmp.Transform()); diff != "" {
+ t.Fatalf("users mismatch (-want +got):\n%s", diff)
+ }
})
}
}
@@ -200,7 +203,9 @@ func TestGetUser(t *testing.T) {
abe2 := NewAuthBackend(lg, be2)
users := abe2.GetUser("alice")
- assert.Equal(t, tc.want, users)
+ if diff := cmp.Diff(tc.want, users, protocmp.Transform()); diff != "" {
+ t.Fatalf("users mismatch (-want +got):\n%s", diff)
+ }
})
}
}
diff --git a/server/storage/schema/cindex.go b/server/storage/schema/cindex.go
index cdf938d346c0..409dcf4233b1 100644
--- a/server/storage/schema/cindex.go
+++ b/server/storage/schema/cindex.go
@@ -16,7 +16,6 @@ package schema
import (
"encoding/binary"
- "fmt"
"go.etcd.io/etcd/client/pkg/v3/verify"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -76,10 +75,11 @@ func unsafeUpdateConsistentIndex(tx backend.UnsafeReadWriter, index uint64, term
binary.BigEndian.PutUint64(bs1, index)
if !allowDecreasing {
- verify.Verify(func() {
+ verify.Verify("update of consistent index not advancing", func() (bool, map[string]any) {
previousIndex, _ := UnsafeReadConsistentIndex(tx)
- if index < previousIndex {
- panic(fmt.Errorf("update of consistent index not advancing: previous: %v new: %v", previousIndex, index))
+ return index >= previousIndex, map[string]any{
+ "previousIndex": previousIndex,
+ "currentIndex": index,
}
})
}
diff --git a/server/storage/schema/confstate_test.go b/server/storage/schema/confstate_test.go
index 11131e40579c..3f1922632efc 100644
--- a/server/storage/schema/confstate_test.go
+++ b/server/storage/schema/confstate_test.go
@@ -35,10 +35,10 @@ func TestConfStateFromBackendInOneTx(t *testing.T) {
defer tx.Unlock()
assert.Nil(t, UnsafeConfStateFromBackend(lg, tx))
- confState := raftpb.ConfState{Learners: []uint64{1, 2}, Voters: []uint64{3}, AutoLeave: false}
+ confState := raftpb.ConfState{Learners: []uint64{1, 2}, Voters: []uint64{3}, AutoLeave: new(false)}
MustUnsafeSaveConfStateToBackend(lg, tx, &confState)
- assert.Equal(t, confState, *UnsafeConfStateFromBackend(lg, tx))
+ assert.Equal(t, &confState, UnsafeConfStateFromBackend(lg, tx))
}
func TestMustUnsafeSaveConfStateToBackend(t *testing.T) {
@@ -59,7 +59,7 @@ func TestMustUnsafeSaveConfStateToBackend(t *testing.T) {
assert.Nil(t, UnsafeConfStateFromBackend(lg, tx))
})
- confState := raftpb.ConfState{Learners: []uint64{1, 2}, Voters: []uint64{3}, AutoLeave: false}
+ confState := raftpb.ConfState{Learners: []uint64{1, 2}, Voters: []uint64{3}, AutoLeave: new(false)}
t.Run("save", func(t *testing.T) {
tx := be.BatchTx()
@@ -73,6 +73,6 @@ func TestMustUnsafeSaveConfStateToBackend(t *testing.T) {
tx := be.ReadTx()
tx.RLock()
defer tx.RUnlock()
- assert.Equal(t, confState, *UnsafeConfStateFromBackend(lg, tx))
+ assert.Equal(t, &confState, UnsafeConfStateFromBackend(lg, tx))
})
}
diff --git a/server/storage/schema/lease.go b/server/storage/schema/lease.go
index de29f30fc494..bc72c562166e 100644
--- a/server/storage/schema/lease.go
+++ b/server/storage/schema/lease.go
@@ -18,6 +18,8 @@ import (
"encoding/binary"
"fmt"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/server/v3/lease/leasepb"
"go.etcd.io/etcd/server/v3/storage/backend"
)
@@ -30,7 +32,7 @@ func MustUnsafeGetAllLeases(tx backend.UnsafeReader) []*leasepb.Lease {
ls := make([]*leasepb.Lease, 0)
err := tx.UnsafeForEach(Lease, func(k, v []byte) error {
var lpb leasepb.Lease
- err := lpb.Unmarshal(v)
+ err := proto.Unmarshal(v, &lpb)
if err != nil {
return fmt.Errorf("failed to Unmarshal lease proto item; lease ID=%016x", bytesToLeaseID(k))
}
@@ -46,7 +48,7 @@ func MustUnsafeGetAllLeases(tx backend.UnsafeReader) []*leasepb.Lease {
func MustUnsafePutLease(tx backend.UnsafeWriter, lpb *leasepb.Lease) {
key := leaseIDToBytes(lpb.ID)
- val, err := lpb.Marshal()
+ val, err := proto.Marshal(lpb)
if err != nil {
panic("failed to marshal lease proto item")
}
@@ -63,7 +65,7 @@ func MustUnsafeGetLease(tx backend.UnsafeReader, leaseID int64) *leasepb.Lease {
return nil
}
var lpb leasepb.Lease
- err := lpb.Unmarshal(vs[0])
+ err := proto.Unmarshal(vs[0], &lpb)
if err != nil {
panic("failed to unmarshal lease proto item")
}
diff --git a/server/storage/schema/lease_test.go b/server/storage/schema/lease_test.go
index 88a1cd7e0633..2065c63ab77f 100644
--- a/server/storage/schema/lease_test.go
+++ b/server/storage/schema/lease_test.go
@@ -19,8 +19,9 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/assert"
+ "github.com/google/go-cmp/cmp"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/server/v3/lease/leasepb"
"go.etcd.io/etcd/server/v3/storage/backend"
@@ -103,7 +104,9 @@ func TestLeaseBackend(t *testing.T) {
defer be2.Close()
leases := MustUnsafeGetAllLeases(be2.ReadTx())
- assert.Equal(t, tc.want, leases)
+ if diff := cmp.Diff(tc.want, leases, protocmp.Transform()); diff != "" {
+ t.Fatalf("leases mismatch (-want +got):\n%s", diff)
+ }
})
}
}
diff --git a/server/storage/schema/membership.go b/server/storage/schema/membership.go
index 8307a318b915..76428cf33ffe 100644
--- a/server/storage/schema/membership.go
+++ b/server/storage/schema/membership.go
@@ -37,6 +37,9 @@ type membershipBackend struct {
be backend.Backend
}
+// NewMembershipBackend returns a new membership backend
+// Refer to https://github.com/etcd-io/etcd/pull/19343#discussion_r1958056718
+// revive:disable-next-line:unexported-return
func NewMembershipBackend(lg *zap.Logger, be backend.Backend) *membershipBackend {
return &membershipBackend{
lg: lg,
diff --git a/server/storage/schema/migration_test.go b/server/storage/schema/migration_test.go
index 2b1d563864de..0f762c406f28 100644
--- a/server/storage/schema/migration_test.go
+++ b/server/storage/schema/migration_test.go
@@ -51,11 +51,16 @@ func TestNewPlan(t *testing.T) {
target: version.V3_5,
},
{
- name: "Upgrade v3.6 to v3.7 should fail as v3.7 is unknown",
- current: version.V3_6,
- target: version.V3_7,
+ name: "Upgrade v3.6 to v3.7 should work",
+ current: version.V3_6,
+ target: version.V3_7,
+ },
+ {
+ name: "Upgrade v3.7 to v3.8 should fail as v3.8 is unknown",
+ current: version.V3_7,
+ target: version.V3_8,
expectError: true,
- expectErrorMsg: `version "3.7.0" is not supported`,
+ expectErrorMsg: `version "3.8.0" is not supported`,
},
{
name: "Upgrade v3.6 to v4.0 as major version changes are unsupported",
diff --git a/server/storage/schema/schema.go b/server/storage/schema/schema.go
index 2150c9ef2da8..607e89e6615a 100644
--- a/server/storage/schema/schema.go
+++ b/server/storage/schema/schema.go
@@ -131,6 +131,7 @@ var (
version.V3_6: {
addNewField(Meta, MetaStorageVersionName, emptyStorageVersion),
},
+ version.V3_7: {},
}
// emptyStorageVersion is used for v3.6 Step for the first time, in all other version StoragetVersion should be set by migrator.
// Adding a addNewField for StorageVersion we can reuse logic to remove it when downgrading to v3.5
diff --git a/server/storage/schema/schema_test.go b/server/storage/schema/schema_test.go
index b3df6d7cd7cc..d7f6e703d4c3 100644
--- a/server/storage/schema/schema_test.go
+++ b/server/storage/schema/schema_test.go
@@ -57,26 +57,38 @@ func TestValidate(t *testing.T) {
name: `V3.5 schema without term field is correct`,
version: version.V3_5,
overrideKeys: func(tx backend.UnsafeReadWriter) {
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
},
},
{
name: `V3.5 schema with all fields is correct`,
version: version.V3_5,
overrideKeys: func(tx backend.UnsafeReadWriter) {
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
UnsafeUpdateConsistentIndex(tx, 1, 1)
},
},
{
name: `V3.6 schema is correct`,
version: version.V3_6,
+ overrideKeys: func(tx backend.UnsafeReadWriter) {
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
+ UnsafeUpdateConsistentIndex(tx, 1, 1)
+ },
},
{
- name: `V3.7 schema is unknown and should return error`,
- version: version.V3_7,
+ name: `V3.7 is correct`,
+ version: version.V3_7,
+ overrideKeys: func(tx backend.UnsafeReadWriter) {
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
+ UnsafeUpdateConsistentIndex(tx, 1, 1)
+ },
+ },
+ {
+ name: `V3.8 schema is unknown and should return error`,
+ version: version.V3_8,
expectError: true,
- expectErrorMsg: `version "3.7.0" is not supported`,
+ expectErrorMsg: `version "3.8.0" is not supported`,
},
}
for _, tc := range tcs {
@@ -104,7 +116,7 @@ func TestMigrate(t *testing.T) {
// Overrides which keys should be set (default based on version)
overrideKeys func(tx backend.UnsafeReadWriter)
targetVersion semver.Version
- walEntries []etcdserverpb.InternalRaftRequest
+ walEntries []*etcdserverpb.InternalRaftRequest
expectVersion *semver.Version
expectError bool
@@ -116,7 +128,7 @@ func TestMigrate(t *testing.T) {
name: `Upgrading v3.5 to v3.6 should be rejected if term is not set`,
version: version.V3_5,
overrideKeys: func(tx backend.UnsafeReadWriter) {
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
},
targetVersion: version.V3_6,
expectVersion: nil,
@@ -148,26 +160,38 @@ func TestMigrate(t *testing.T) {
expectVersion: &version.V3_7,
},
{
- name: "Upgrading 3.6 to v3.7 is not supported",
- version: version.V3_6,
- targetVersion: version.V3_7,
- expectVersion: &version.V3_6,
- expectError: true,
- expectErrorMsg: `cannot create migration plan: version "3.7.0" is not supported`,
+ name: "Upgrading 3.6 to v3.7 should work",
+ version: version.V3_6,
+ targetVersion: version.V3_7,
+ expectVersion: &version.V3_7,
},
{
- name: "Downgrading v3.7 to v3.6 is not supported",
+ name: "Upgrading 3.7 to v3.8 is not supported",
version: version.V3_7,
- targetVersion: version.V3_6,
+ targetVersion: version.V3_8,
expectVersion: &version.V3_7,
expectError: true,
- expectErrorMsg: `cannot create migration plan: version "3.7.0" is not supported`,
+ expectErrorMsg: `cannot create migration plan: version "3.8.0" is not supported`,
+ },
+ {
+ name: "Downgrading v3.7 to v3.6 should work",
+ version: version.V3_7,
+ targetVersion: version.V3_6,
+ expectVersion: &version.V3_6,
+ },
+ {
+ name: "Downgrading v3.8 to v3.7 is not supported",
+ version: version.V3_8,
+ targetVersion: version.V3_7,
+ expectVersion: &version.V3_8,
+ expectError: true,
+ expectErrorMsg: `cannot create migration plan: version "3.8.0" is not supported`,
},
{
name: "Downgrading v3.6 to v3.5 works as there are no v3.6 wal entries",
version: version.V3_6,
targetVersion: version.V3_5,
- walEntries: []etcdserverpb.InternalRaftRequest{
+ walEntries: []*etcdserverpb.InternalRaftRequest{
{Range: &etcdserverpb.RangeRequest{Key: []byte("\x00"), RangeEnd: []byte("\xff")}},
},
expectVersion: nil,
@@ -176,7 +200,7 @@ func TestMigrate(t *testing.T) {
name: "Downgrading v3.6 to v3.5 fails if there are newer WAL entries",
version: version.V3_6,
targetVersion: version.V3_5,
- walEntries: []etcdserverpb.InternalRaftRequest{
+ walEntries: []*etcdserverpb.InternalRaftRequest{
{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.6.0"}},
},
expectVersion: &version.V3_6,
@@ -299,17 +323,22 @@ func setupBackendData(t *testing.T, ver semver.Version, overrideKeys func(tx bac
switch ver {
case version.V3_4:
case version.V3_5:
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
UnsafeUpdateConsistentIndex(tx, 1, 1)
case version.V3_6:
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
UnsafeUpdateConsistentIndex(tx, 1, 1)
UnsafeSetStorageVersion(tx, &version.V3_6)
case version.V3_7:
- MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{})
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
UnsafeUpdateConsistentIndex(tx, 1, 1)
UnsafeSetStorageVersion(tx, &version.V3_7)
tx.UnsafePut(Meta, []byte("future-key"), []byte(""))
+ case version.V3_8:
+ MustUnsafeSaveConfStateToBackend(zap.NewNop(), tx, &raftpb.ConfState{AutoLeave: new(false)})
+ UnsafeUpdateConsistentIndex(tx, 1, 1)
+ UnsafeSetStorageVersion(tx, &version.V3_8)
+ tx.UnsafePut(Meta, []byte("future-key"), []byte(""))
default:
t.Fatalf("Unsupported storage version")
}
diff --git a/server/storage/storage.go b/server/storage/storage.go
index 99a37a23d071..8fd5c1cb367d 100644
--- a/server/storage/storage.go
+++ b/server/storage/storage.go
@@ -30,13 +30,13 @@ import (
type Storage interface {
// Save function saves ents and state to the underlying stable storage.
// Save MUST block until st and ents are on stable storage.
- Save(st raftpb.HardState, ents []raftpb.Entry) error
+ Save(st *raftpb.HardState, ents []*raftpb.Entry) error
// SaveSnap function saves snapshot to the underlying stable storage.
- SaveSnap(snap raftpb.Snapshot) error
+ SaveSnap(snap *raftpb.Snapshot) error
// Close closes the Storage and performs finalization.
Close() error
// Release releases the locked wal files older than the provided snapshot.
- Release(snap raftpb.Snapshot) error
+ Release(snap *raftpb.Snapshot) error
// Sync WAL
Sync() error
// MinimalEtcdVersion returns minimal etcd storage able to interpret WAL log.
@@ -57,13 +57,13 @@ func NewStorage(lg *zap.Logger, w *wal.WAL, s *snap.Snapshotter) Storage {
}
// SaveSnap saves the snapshot file to disk and writes the WAL snapshot entry.
-func (st *storage) SaveSnap(snap raftpb.Snapshot) error {
+func (st *storage) SaveSnap(snap *raftpb.Snapshot) error {
st.mux.RLock()
defer st.mux.RUnlock()
walsnap := walpb.Snapshot{
Index: snap.Metadata.Index,
Term: snap.Metadata.Term,
- ConfState: &snap.Metadata.ConfState,
+ ConfState: snap.Metadata.GetConfState(),
}
// save the snapshot file before writing the snapshot to the wal.
// This makes it possible for the snapshot file to become orphaned, but prevents
@@ -74,22 +74,22 @@ func (st *storage) SaveSnap(snap raftpb.Snapshot) error {
}
// gofail: var raftBeforeWALSaveSnaphot struct{}
- return st.w.SaveSnapshot(walsnap)
+ return st.w.SaveSnapshot(&walsnap)
}
// Release releases resources older than the given snap and are no longer needed:
// - releases the locks to the wal files that are older than the provided wal for the given snap.
// - deletes any .snap.db files that are older than the given snap.
-func (st *storage) Release(snap raftpb.Snapshot) error {
+func (st *storage) Release(snap *raftpb.Snapshot) error {
st.mux.RLock()
defer st.mux.RUnlock()
- if err := st.w.ReleaseLockTo(snap.Metadata.Index); err != nil {
+ if err := st.w.ReleaseLockTo(snap.Metadata.GetIndex()); err != nil {
return err
}
return st.s.ReleaseSnapDBs(snap)
}
-func (st *storage) Save(s raftpb.HardState, ents []raftpb.Entry) error {
+func (st *storage) Save(s *raftpb.HardState, ents []*raftpb.Entry) error {
st.mux.RLock()
defer st.mux.RUnlock()
return st.w.Save(s, ents)
@@ -119,9 +119,9 @@ func (st *storage) MinimalEtcdVersion() *semver.Version {
if sn != nil {
walsnap.Index = sn.Metadata.Index
walsnap.Term = sn.Metadata.Term
- walsnap.ConfState = &sn.Metadata.ConfState
+ walsnap.ConfState = sn.Metadata.GetConfState()
}
- w, err := st.w.Reopen(st.lg, walsnap)
+ w, err := st.w.Reopen(st.lg, &walsnap)
if err != nil {
panic(err)
}
diff --git a/server/storage/util.go b/server/storage/util.go
index b6ca2ac88e5c..6a8cae1a1fbc 100644
--- a/server/storage/util.go
+++ b/server/storage/util.go
@@ -16,42 +16,22 @@ package storage
import (
"encoding/json"
- "fmt"
"sort"
"go.uber.org/zap"
"go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/pbutil"
- "go.etcd.io/etcd/server/v3/config"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
- "go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
"go.etcd.io/raft/v3/raftpb"
)
-// AssertNoV2StoreContent -> depending on the deprecation stage, warns or report an error
-// if the v2store contains custom content.
-func AssertNoV2StoreContent(lg *zap.Logger, st v2store.Store, deprecationStage config.V2DeprecationEnum) error {
- metaOnly, err := membership.IsMetaStoreOnly(st)
- if err != nil {
- return err
- }
- if metaOnly {
- return nil
- }
- if deprecationStage.IsAtLeast(config.V2Depr1WriteOnly) {
- return fmt.Errorf("detected disallowed custom content in v2store for stage --v2-deprecation=%s", deprecationStage)
- }
- lg.Warn("detected custom v2store content. Etcd v3.5 is the last version allowing to access it using API v2. Please remove the content.")
- return nil
-}
-
// CreateConfigChangeEnts creates a series of Raft entries (i.e.
// EntryConfChange) to remove the set of given IDs from the cluster. The ID
// `self` is _not_ removed, even if present in the set.
// If `self` is not inside the given ids, it creates a Raft entry to add a
// default member with the given `self`.
-func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, index uint64) []raftpb.Entry {
+func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, index uint64) []*raftpb.Entry {
found := false
for _, id := range ids {
if id == self {
@@ -59,7 +39,7 @@ func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
}
}
- var ents []raftpb.Entry
+ var ents []*raftpb.Entry
next := index + 1
// NB: always add self first, then remove other nodes. Raft will panic if the
@@ -74,15 +54,16 @@ func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
lg.Panic("failed to marshal member", zap.Error(err))
}
cc := &raftpb.ConfChange{
- Type: raftpb.ConfChangeAddNode,
- NodeID: self,
+ Type: raftpb.ConfChangeAddNode.Enum(),
+ NodeId: &self,
Context: ctx,
}
- e := raftpb.Entry{
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(cc),
- Term: term,
- Index: next,
+ idx := next
+ e := &raftpb.Entry{
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(cc),
+ Term: new(term),
+ Index: new(idx),
}
ents = append(ents, e)
next++
@@ -93,14 +74,16 @@ func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
continue
}
cc := &raftpb.ConfChange{
- Type: raftpb.ConfChangeRemoveNode,
- NodeID: id,
+ Type: raftpb.ConfChangeRemoveNode.Enum(),
+ NodeId: new(uint64),
}
- e := raftpb.Entry{
- Type: raftpb.EntryConfChange,
- Data: pbutil.MustMarshal(cc),
- Term: term,
- Index: next,
+ *cc.NodeId = id
+ idx := next
+ e := &raftpb.Entry{
+ Type: raftpb.EntryConfChange.Enum(),
+ Data: pbutil.MustMarshalMessage(cc),
+ Term: new(term),
+ Index: new(idx),
}
ents = append(ents, e)
next++
@@ -109,49 +92,40 @@ func CreateConfigChangeEnts(lg *zap.Logger, ids []uint64, self uint64, term, ind
return ents
}
-// GetEffectiveNodeIdsFromWalEntries returns an ordered set of IDs included in the given snapshot and
-// the entries.
-//
-// Deprecated: use GetEffectiveNodeIDsFromWALEntries instead.
-//
-//revive:disable-next-line:var-naming
-func GetEffectiveNodeIdsFromWalEntries(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 {
- return GetEffectiveNodeIDsFromWALEntries(lg, snap, ents)
-}
-
// GetEffectiveNodeIDsFromWALEntries returns an ordered set of IDs included in the given snapshot and
// the entries. The given snapshot/entries can contain three kinds of
// ID-related entry:
// - ConfChangeAddNode, in which case the contained ID will Be added into the set.
// - ConfChangeRemoveNode, in which case the contained ID will Be removed from the set.
// - ConfChangeAddLearnerNode, in which the contained ID will Be added into the set.
-func GetEffectiveNodeIDsFromWALEntries(lg *zap.Logger, snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 {
+func GetEffectiveNodeIDsFromWALEntries(lg *zap.Logger, snap *raftpb.Snapshot, ents []*raftpb.Entry) []uint64 {
ids := make(map[uint64]bool)
- if snap != nil {
- for _, id := range snap.Metadata.ConfState.Voters {
+ if snap.GetMetadata().GetConfState() != nil {
+ for _, id := range snap.GetMetadata().GetConfState().GetVoters() {
ids[id] = true
}
- for _, id := range snap.Metadata.ConfState.Learners {
+ for _, id := range snap.GetMetadata().GetConfState().GetLearners() {
ids[id] = true
}
}
- for _, e := range ents {
- if e.Type != raftpb.EntryConfChange {
+ for i := range ents {
+ e := ents[i]
+ if e.GetType() != raftpb.EntryConfChange {
continue
}
var cc raftpb.ConfChange
- pbutil.MustUnmarshal(&cc, e.Data)
- switch cc.Type {
+ pbutil.MustUnmarshalMessage(&cc, e.Data)
+ switch cc.GetType() {
case raftpb.ConfChangeAddLearnerNode:
- ids[cc.NodeID] = true
+ ids[cc.GetNodeId()] = true
case raftpb.ConfChangeAddNode:
- ids[cc.NodeID] = true
+ ids[cc.GetNodeId()] = true
case raftpb.ConfChangeRemoveNode:
- delete(ids, cc.NodeID)
+ delete(ids, cc.GetNodeId())
case raftpb.ConfChangeUpdateNode:
// do nothing
default:
- lg.Panic("unknown ConfChange Type", zap.String("type", cc.Type.String()))
+ lg.Panic("unknown ConfChange Type", zap.String("type", cc.GetType().String()))
}
}
sids := make(types.Uint64Slice, 0, len(ids))
diff --git a/server/storage/wal/decoder.go b/server/storage/wal/decoder.go
index bdd4962e97f7..b23dfb3d48e5 100644
--- a/server/storage/wal/decoder.go
+++ b/server/storage/wal/decoder.go
@@ -22,6 +22,8 @@ import (
"io"
"sync"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/pkg/v3/crc"
"go.etcd.io/etcd/pkg/v3/pbutil"
@@ -120,7 +122,7 @@ func (d *decoder) decodeRecord(rec *walpb.Record) error {
}
return err
}
- if err := rec.Unmarshal(data[:recBytes]); err != nil {
+ if err := proto.Unmarshal(data[:recBytes], rec); err != nil {
if d.isTornEntry(data) {
return io.ErrUnexpectedEOF
}
@@ -128,7 +130,7 @@ func (d *decoder) decodeRecord(rec *walpb.Record) error {
}
// skip crc checking if the record type is CrcType
- if rec.Type != CrcType {
+ if rec.GetType() != CrcType {
_, err := d.crc.Write(rec.Data)
if err != nil {
return err
@@ -210,16 +212,16 @@ func (d *decoder) LastCRC() uint32 {
func (d *decoder) LastOffset() int64 { return d.lastValidOff }
-func MustUnmarshalEntry(d []byte) raftpb.Entry {
+func MustUnmarshalEntry(d []byte) *raftpb.Entry {
var e raftpb.Entry
- pbutil.MustUnmarshal(&e, d)
- return e
+ pbutil.MustUnmarshalMessage(&e, d)
+ return &e
}
-func MustUnmarshalState(d []byte) raftpb.HardState {
+func MustUnmarshalState(d []byte) *raftpb.HardState {
var s raftpb.HardState
- pbutil.MustUnmarshal(&s, d)
- return s
+ pbutil.MustUnmarshalMessage(&s, d)
+ return &s
}
func readInt64(r io.Reader) (int64, error) {
diff --git a/server/storage/wal/doc.go b/server/storage/wal/doc.go
index 0f7ef8527bea..977ca3445004 100644
--- a/server/storage/wal/doc.go
+++ b/server/storage/wal/doc.go
@@ -28,7 +28,9 @@ to it with the Save method:
After saving a raft snapshot to disk, SaveSnapshot method should be called to
record it. So WAL can match with the saved snapshot when restarting.
- err := w.SaveSnapshot(walpb.Snapshot{Index: 10, Term: 2})
+ index := uint64(10)
+ term := uint64(2)
+ err := w.SaveSnapshot(&walpb.Snapshot{Index: new(index), Term: new(term)})
When a user has finished using a WAL it must be closed:
@@ -56,9 +58,11 @@ If a second cut issues 0x10 entries with incremental index later, then the file
0000000000000002-0000000000000031.wal.
At a later time a WAL can be opened at a particular snapshot. If there is no
-snapshot, an empty snapshot should be passed in.
+snapshot, nil or an empty snapshot should be passed in.
- w, err := wal.Open("/var/lib/etcd", walpb.Snapshot{Index: 10, Term: 2})
+ index := uint64(10)
+ term := uint64(2)
+ w, err := wal.Open(zap.NewExample(), "/var/lib/etcd", &walpb.Snapshot{Index: &index, Term: &term})
...
The snapshot must have been written to the WAL.
diff --git a/server/storage/wal/encoder.go b/server/storage/wal/encoder.go
index 5944ba7b120f..3c9323364712 100644
--- a/server/storage/wal/encoder.go
+++ b/server/storage/wal/encoder.go
@@ -16,12 +16,15 @@ package wal
import (
"encoding/binary"
+ "errors"
"hash"
"io"
"os"
"sync"
"time"
+ "google.golang.org/protobuf/proto"
+
"go.etcd.io/etcd/pkg/v3/crc"
"go.etcd.io/etcd/pkg/v3/ioutil"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
@@ -61,28 +64,32 @@ func newFileEncoder(f *os.File, prevCrc uint32) (*encoder, error) {
}
func (e *encoder) encode(rec *walpb.Record) error {
+ if rec.Type == nil {
+ return errors.New("record is missing type")
+ }
+
e.mu.Lock()
defer e.mu.Unlock()
e.crc.Write(rec.Data)
- rec.Crc = e.crc.Sum32()
+ rec.Crc = new(e.crc.Sum32())
var (
data []byte
err error
- n int
)
- if rec.Size() > len(e.buf) {
- data, err = rec.Marshal()
+ size := proto.Size(rec)
+ opts := proto.MarshalOptions{UseCachedSize: true}
+ if size > len(e.buf) {
+ data, err = proto.Marshal(rec)
if err != nil {
return err
}
} else {
- n, err = rec.MarshalTo(e.buf)
+ data, err = opts.MarshalAppend(e.buf[:0], rec)
if err != nil {
return err
}
- data = e.buf[:n]
}
data, lenField := prepareDataWithPadding(data)
diff --git a/server/storage/wal/record_test.go b/server/storage/wal/record_test.go
index 85ceebed9c11..fc533961993e 100644
--- a/server/storage/wal/record_test.go
+++ b/server/storage/wal/record_test.go
@@ -20,9 +20,11 @@ import (
"hash/crc32"
"io"
"os"
- "reflect"
"testing"
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
+
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
)
@@ -42,7 +44,7 @@ func TestReadRecord(t *testing.T) {
wr *walpb.Record
we error
}{
- {infoRecord, &walpb.Record{Type: 1, Crc: crc32.Checksum(infoData, crcTable), Data: infoData}, nil},
+ {infoRecord, &walpb.Record{Type: new(int64(1)), Crc: new(crc32.Checksum(infoData, crcTable)), Data: infoData}, nil},
{[]byte(""), &walpb.Record{}, io.EOF},
{infoRecord[:14], &walpb.Record{}, io.ErrUnexpectedEOF},
{infoRecord[:len(infoRecord)-len(infoData)], &walpb.Record{}, io.ErrUnexpectedEOF},
@@ -59,8 +61,8 @@ func TestReadRecord(t *testing.T) {
}
decoder := NewDecoder(fileutil.NewFileReader(f))
e := decoder.Decode(rec)
- if !reflect.DeepEqual(rec, tt.wr) {
- t.Errorf("#%d: block = %v, want %v", i, rec, tt.wr)
+ if diff := cmp.Diff(tt.wr, rec, protocmp.Transform()); diff != "" {
+ t.Errorf("#%d: block mismatch (-want +got):\n%s", i, diff)
}
if !errors.Is(e, tt.we) {
t.Errorf("#%d: err = %v, want %v", i, e, tt.we)
@@ -75,7 +77,7 @@ func TestWriteRecord(t *testing.T) {
d := []byte("Hello world!")
buf := new(bytes.Buffer)
e := newEncoder(buf, 0, 0)
- e.encode(&walpb.Record{Type: typ, Data: d})
+ e.encode(&walpb.Record{Type: new(typ), Data: d})
e.flush()
f, err := createFileWithData(t, buf)
if err != nil {
@@ -86,10 +88,10 @@ func TestWriteRecord(t *testing.T) {
if err != nil {
t.Errorf("err = %v, want nil", err)
}
- if b.Type != typ {
+ if b.GetType() != typ {
t.Errorf("type = %d, want %d", b.Type, typ)
}
- if !reflect.DeepEqual(b.Data, d) {
+ if !bytes.Equal(b.Data, d) {
t.Errorf("data = %v, want %v", b.Data, d)
}
}
diff --git a/server/storage/wal/repair.go b/server/storage/wal/repair.go
index 16277540f34d..0cdfd006febf 100644
--- a/server/storage/wal/repair.go
+++ b/server/storage/wal/repair.go
@@ -49,15 +49,14 @@ func Repair(lg *zap.Logger, dirpath string) bool {
switch {
case err == nil:
// update crc of the decoder when necessary
- switch rec.Type {
- case CrcType:
+ if rec.GetType() == CrcType {
crc := decoder.LastCRC()
// current crc of decoder must match the crc of the record.
// do no need to match 0 crc, since the decoder is a new one at this case.
if crc != 0 && rec.Validate(crc) != nil {
return false
}
- decoder.UpdateCRC(rec.Crc)
+ decoder.UpdateCRC(rec.GetCrc())
}
continue
diff --git a/server/storage/wal/repair_test.go b/server/storage/wal/repair_test.go
index 8f05b197806f..1aac5d5a1b07 100644
--- a/server/storage/wal/repair_test.go
+++ b/server/storage/wal/repair_test.go
@@ -46,7 +46,7 @@ func TestRepairTruncate(t *testing.T) {
testRepair(t, makeEnts(10), corruptf, 9)
}
-func testRepair(t *testing.T, ents [][]raftpb.Entry, corrupt corruptFunc, expectedEnts int) {
+func testRepair(t *testing.T, ents [][]*raftpb.Entry, corrupt corruptFunc, expectedEnts int) {
lg := zaptest.NewLogger(t)
p := t.TempDir()
@@ -59,7 +59,7 @@ func testRepair(t *testing.T, ents [][]raftpb.Entry, corrupt corruptFunc, expect
require.NoError(t, err)
for _, es := range ents {
- require.NoError(t, w.Save(raftpb.HardState{}, es))
+ require.NoError(t, w.Save(&raftpb.HardState{}, es))
}
offset, err := w.tail().Seek(0, io.SeekCurrent)
@@ -69,7 +69,7 @@ func testRepair(t *testing.T, ents [][]raftpb.Entry, corrupt corruptFunc, expect
require.NoError(t, corrupt(p, offset))
// verify we broke the wal
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
require.NoError(t, err)
_, _, _, err = w.ReadAll()
@@ -88,7 +88,7 @@ func testRepair(t *testing.T, ents [][]raftpb.Entry, corrupt corruptFunc, expect
require.Equalf(t, expectedPerms, actualPerms, "unexpected file permissions on .broken wal")
// read it back
- w, err = Open(lg, p, walpb.Snapshot{})
+ w, err = Open(lg, p, &walpb.Snapshot{})
require.NoError(t, err)
_, _, walEnts, err := w.ReadAll()
@@ -97,22 +97,22 @@ func testRepair(t *testing.T, ents [][]raftpb.Entry, corrupt corruptFunc, expect
// write some more entries to repaired log
for i := 1; i <= 10; i++ {
- es := []raftpb.Entry{{Index: uint64(expectedEnts + i)}}
- require.NoError(t, w.Save(raftpb.HardState{}, es))
+ es := []*raftpb.Entry{{Index: new(uint64(expectedEnts + i))}}
+ require.NoError(t, w.Save(&raftpb.HardState{}, es))
}
require.NoError(t, w.Close())
// read back entries following repair, ensure it's all there
- w, err = Open(lg, p, walpb.Snapshot{})
+ w, err = Open(lg, p, &walpb.Snapshot{})
require.NoError(t, err)
_, _, walEnts, err = w.ReadAll()
require.NoError(t, err)
assert.Len(t, walEnts, expectedEnts+10)
}
-func makeEnts(ents int) (ret [][]raftpb.Entry) {
+func makeEnts(ents int) (ret [][]*raftpb.Entry) {
for i := 1; i <= ents; i++ {
- ret = append(ret, []raftpb.Entry{{Index: uint64(i)}})
+ ret = append(ret, []*raftpb.Entry{{Index: new(uint64(i))}})
}
return ret
}
@@ -177,7 +177,7 @@ func TestRepairFailDeleteDir(t *testing.T) {
SegmentSizeBytes = oldSegmentSizeBytes
}()
for _, es := range makeEnts(50) {
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
}
@@ -197,7 +197,7 @@ func TestRepairFailDeleteDir(t *testing.T) {
}
f.Close()
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
diff --git a/server/storage/wal/testdata/TestNew.wal b/server/storage/wal/testdata/TestNew.wal
new file mode 100644
index 000000000000..fda4671635c4
Binary files /dev/null and b/server/storage/wal/testdata/TestNew.wal differ
diff --git a/server/storage/wal/testing/waltesting.go b/server/storage/wal/testing/waltesting.go
index bd1dbaade636..8e141d6eab02 100644
--- a/server/storage/wal/testing/waltesting.go
+++ b/server/storage/wal/testing/waltesting.go
@@ -28,65 +28,65 @@ import (
"go.etcd.io/raft/v3/raftpb"
)
-func NewTmpWAL(t testing.TB, reqs []etcdserverpb.InternalRaftRequest) (*wal.WAL, string) {
- t.Helper()
- dir, err := os.MkdirTemp(t.TempDir(), "etcd_wal_test")
+func NewTmpWAL(tb testing.TB, reqs []*etcdserverpb.InternalRaftRequest) (*wal.WAL, string) {
+ tb.Helper()
+ dir, err := os.MkdirTemp(tb.TempDir(), "etcd_wal_test")
if err != nil {
panic(err)
}
tmpPath := filepath.Join(dir, "wal")
- lg := zaptest.NewLogger(t)
+ lg := zaptest.NewLogger(tb)
w, err := wal.Create(lg, tmpPath, nil)
if err != nil {
- t.Fatalf("Failed to create WAL: %v", err)
+ tb.Fatalf("Failed to create WAL: %v", err)
}
err = w.Close()
if err != nil {
- t.Fatalf("Failed to close WAL: %v", err)
+ tb.Fatalf("Failed to close WAL: %v", err)
}
if len(reqs) != 0 {
- w, err = wal.Open(lg, tmpPath, walpb.Snapshot{})
+ w, err = wal.Open(lg, tmpPath, &walpb.Snapshot{})
if err != nil {
- t.Fatalf("Failed to open WAL: %v", err)
+ tb.Fatalf("Failed to open WAL: %v", err)
}
- var state raftpb.HardState
+ var state *raftpb.HardState
_, state, _, err = w.ReadAll()
if err != nil {
- t.Fatalf("Failed to read WAL: %v", err)
+ tb.Fatalf("Failed to read WAL: %v", err)
}
- var entries []raftpb.Entry
+ var entries []*raftpb.Entry
for _, req := range reqs {
- entries = append(entries, raftpb.Entry{
- Term: 1,
- Index: 1,
- Type: raftpb.EntryNormal,
- Data: pbutil.MustMarshal(&req),
+ entries = append(entries, &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(1)),
+ Type: raftpb.EntryNormal.Enum(),
+ Data: pbutil.MustMarshalMessage(req),
})
}
err = w.Save(state, entries)
if err != nil {
- t.Fatalf("Failed to save WAL: %v", err)
+ tb.Fatalf("Failed to save WAL: %v", err)
}
err = w.Close()
if err != nil {
- t.Fatalf("Failed to close WAL: %v", err)
+ tb.Fatalf("Failed to close WAL: %v", err)
}
}
- w, err = wal.OpenForRead(lg, tmpPath, walpb.Snapshot{})
+ w, err = wal.OpenForRead(lg, tmpPath, &walpb.Snapshot{})
if err != nil {
- t.Fatalf("Failed to open WAL: %v", err)
+ tb.Fatalf("Failed to open WAL: %v", err)
}
return w, tmpPath
}
-func Reopen(t testing.TB, walPath string) *wal.WAL {
- t.Helper()
- lg := zaptest.NewLogger(t)
- w, err := wal.OpenForRead(lg, walPath, walpb.Snapshot{})
+func Reopen(tb testing.TB, walPath string) *wal.WAL {
+ tb.Helper()
+ lg := zaptest.NewLogger(tb)
+ w, err := wal.OpenForRead(lg, walPath, &walpb.Snapshot{})
if err != nil {
- t.Fatalf("Failed to open WAL: %v", err)
+ tb.Fatalf("Failed to open WAL: %v", err)
}
return w
}
diff --git a/server/storage/wal/version.go b/server/storage/wal/version.go
index b592fd2ee86c..e3b117c50cc0 100644
--- a/server/storage/wal/version.go
+++ b/server/storage/wal/version.go
@@ -19,13 +19,12 @@ import (
"strings"
"github.com/coreos/go-semver/semver"
- "github.com/golang/protobuf/proto"
+ "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/version"
- "go.etcd.io/etcd/pkg/v3/pbutil"
"go.etcd.io/raft/v3/raftpb"
)
@@ -46,7 +45,7 @@ func ReadWALVersion(w *WAL) (Version, error) {
}
type walVersion struct {
- entries []raftpb.Entry
+ entries []*raftpb.Entry
}
// MinimalEtcdVersion returns minimal etcd able to interpret entries from WAL log,
@@ -57,7 +56,7 @@ func (w *walVersion) MinimalEtcdVersion() *semver.Version {
// MinimalEtcdVersion returns minimal etcd able to interpret entries from WAL log,
// determined by looking at entries since the last snapshot and returning the highest
// etcd version annotation from used messages, fields, enums and their values.
-func MinimalEtcdVersion(ents []raftpb.Entry) *semver.Version {
+func MinimalEtcdVersion(ents []*raftpb.Entry) *semver.Version {
var maxVer *semver.Version
for _, ent := range ents {
err := visitEntry(ent, func(path protoreflect.FullName, ver *semver.Version) error {
@@ -94,12 +93,12 @@ func VisitFileDescriptor(file protoreflect.FileDescriptor, visitor Visitor) erro
return nil
}
-func visitEntry(ent raftpb.Entry, visitor Visitor) error {
- err := visitMessage(proto.MessageReflect(&ent), visitor)
+func visitEntry(ent *raftpb.Entry, visitor Visitor) error {
+ err := visitMessage(proto.MessageReflect(ent), visitor)
if err != nil {
return err
}
- return visitEntryData(ent.Type, ent.Data, visitor)
+ return visitEntryData(ent.GetType(), ent.Data, visitor)
}
func visitEntryData(entryType raftpb.EntryType, data []byte, visitor Visitor) error {
@@ -107,15 +106,8 @@ func visitEntryData(entryType raftpb.EntryType, data []byte, visitor Visitor) er
switch entryType {
case raftpb.EntryNormal:
var raftReq etcdserverpb.InternalRaftRequest
- if err := pbutil.Unmarshaler(&raftReq).Unmarshal(data); err != nil {
- // try V2 Request
- var r etcdserverpb.Request
- if pbutil.Unmarshaler(&r).Unmarshal(data) != nil {
- // return original error
- return err
- }
- msg = proto.MessageReflect(&r)
- break
+ if err := proto.Unmarshal(data, &raftReq); err != nil {
+ return err
}
msg = proto.MessageReflect(&raftReq)
if raftReq.DowngradeVersionTest != nil {
@@ -130,7 +122,7 @@ func visitEntryData(entryType raftpb.EntryType, data []byte, visitor Visitor) er
}
case raftpb.EntryConfChange:
var confChange raftpb.ConfChange
- err := pbutil.Unmarshaler(&confChange).Unmarshal(data)
+ err := proto.Unmarshal(data, &confChange)
if err != nil {
return nil
}
@@ -138,7 +130,7 @@ func visitEntryData(entryType raftpb.EntryType, data []byte, visitor Visitor) er
return visitor(msg.Descriptor().FullName(), &version.V3_0)
case raftpb.EntryConfChangeV2:
var confChange raftpb.ConfChangeV2
- err := pbutil.Unmarshaler(&confChange).Unmarshal(data)
+ err := proto.Unmarshal(data, &confChange)
if err != nil {
return nil
}
diff --git a/server/storage/wal/version_test.go b/server/storage/wal/version_test.go
index 1ee89dd36918..30f6072a9cf6 100644
--- a/server/storage/wal/version_test.go
+++ b/server/storage/wal/version_test.go
@@ -19,9 +19,9 @@ import (
"testing"
"github.com/coreos/go-semver/semver"
- "github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"go.etcd.io/etcd/api/v3/etcdserverpb"
@@ -33,71 +33,71 @@ import (
func TestEtcdVersionFromEntry(t *testing.T) {
raftReq := etcdserverpb.InternalRaftRequest{Header: &etcdserverpb.RequestHeader{AuthRevision: 1}}
- normalRequestData := pbutil.MustMarshal(&raftReq)
+ normalRequestData := pbutil.MustMarshalMessage(&raftReq)
downgradeVersionTestV3_6Req := etcdserverpb.InternalRaftRequest{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.6.0"}}
- downgradeVersionTestV3_6Data := pbutil.MustMarshal(&downgradeVersionTestV3_6Req)
+ downgradeVersionTestV3_6Data := pbutil.MustMarshalMessage(&downgradeVersionTestV3_6Req)
downgradeVersionTestV3_7Req := etcdserverpb.InternalRaftRequest{DowngradeVersionTest: &etcdserverpb.DowngradeVersionTestRequest{Ver: "3.7.0"}}
- downgradeVersionTestV3_7Data := pbutil.MustMarshal(&downgradeVersionTestV3_7Req)
+ downgradeVersionTestV3_7Data := pbutil.MustMarshalMessage(&downgradeVersionTestV3_7Req)
- confChange := raftpb.ConfChange{Type: raftpb.ConfChangeAddLearnerNode}
- confChangeData := pbutil.MustMarshal(&confChange)
+ confChange := raftpb.ConfChange{Type: raftpb.ConfChangeAddLearnerNode.Enum()}
+ confChangeData := pbutil.MustMarshalMessage(&confChange)
- confChangeV2 := raftpb.ConfChangeV2{Transition: raftpb.ConfChangeTransitionJointExplicit}
- confChangeV2Data := pbutil.MustMarshal(&confChangeV2)
+ confChangeV2 := raftpb.ConfChangeV2{Transition: raftpb.ConfChangeTransitionJointExplicit.Enum()}
+ confChangeV2Data := pbutil.MustMarshalMessage(&confChangeV2)
tcs := []struct {
name string
- input raftpb.Entry
+ input *raftpb.Entry
expect *semver.Version
}{
{
name: "Using RequestHeader AuthRevision in NormalEntry implies v3.1",
- input: raftpb.Entry{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryNormal,
+ input: &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryNormal.Enum(),
Data: normalRequestData,
},
expect: &version.V3_1,
},
{
name: "Setting downgradeTest version to 3.6 implies version within WAL",
- input: raftpb.Entry{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryNormal,
+ input: &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryNormal.Enum(),
Data: downgradeVersionTestV3_6Data,
},
expect: &version.V3_6,
},
{
name: "Setting downgradeTest version to 3.7 implies version within WAL",
- input: raftpb.Entry{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryNormal,
+ input: &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryNormal.Enum(),
Data: downgradeVersionTestV3_7Data,
},
expect: &version.V3_7,
},
{
name: "Using ConfigChange implies v3.0",
- input: raftpb.Entry{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryConfChange,
+ input: &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryConfChange.Enum(),
Data: confChangeData,
},
expect: &version.V3_0,
},
{
name: "Using ConfigChangeV2 implies v3.4",
- input: raftpb.Entry{
- Term: 1,
- Index: 2,
- Type: raftpb.EntryConfChangeV2,
+ input: &raftpb.Entry{
+ Term: new(uint64(1)),
+ Index: new(uint64(2)),
+ Type: raftpb.EntryConfChangeV2.Enum(),
Data: confChangeV2Data,
},
expect: &version.V3_4,
@@ -176,7 +176,7 @@ func TestEtcdVersionFromMessage(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
var maxVer *semver.Version
- err := visitMessage(proto.MessageReflect(tc.input), func(path protoreflect.FullName, ver *semver.Version) error {
+ err := visitMessage(tc.input.ProtoReflect(), func(path protoreflect.FullName, ver *semver.Version) error {
maxVer = maxVersion(maxVer, ver)
return nil
})
diff --git a/server/storage/wal/wal.go b/server/storage/wal/wal.go
index f3d7bc5f4d44..e6c1a5d1e72c 100644
--- a/server/storage/wal/wal.go
+++ b/server/storage/wal/wal.go
@@ -77,12 +77,12 @@ type WAL struct {
// dirFile is a fd for the wal directory for syncing on Rename
dirFile *os.File
- metadata []byte // metadata recorded at the head of each WAL
- state raftpb.HardState // hardstate recorded at the head of WAL
+ metadata []byte // metadata recorded at the head of each WAL
+ state *raftpb.HardState // hardstate recorded at the head of WAL
- start walpb.Snapshot // snapshot to start reading
- decoder Decoder // decoder to Decode records
- readClose func() error // closer for Decode reader
+ start *walpb.Snapshot // snapshot to start reading
+ decoder Decoder // decoder to Decode records
+ readClose func() error // closer for Decode reader
unsafeNoSync bool // if set, do not fsync
@@ -166,10 +166,11 @@ func Create(lg *zap.Logger, dirpath string, metadata []byte) (*WAL, error) {
if err = w.saveCrc(0); err != nil {
return nil, err
}
- if err = w.encoder.encode(&walpb.Record{Type: MetadataType, Data: metadata}); err != nil {
+ if err = w.encoder.encode(&walpb.Record{Type: new(MetadataType), Data: metadata}); err != nil {
return nil, err
}
- if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ // Create an empty snapshot record during the initial bootstrap only.
+ if err = w.SaveSnapshot(&walpb.Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}); err != nil {
return nil, err
}
@@ -258,7 +259,7 @@ func createNewWALFile[T *os.File | *fileutil.LockedFile](path string, forceNew b
return any(file).(T), nil
}
-func (w *WAL) Reopen(lg *zap.Logger, snap walpb.Snapshot) (*WAL, error) {
+func (w *WAL) Reopen(lg *zap.Logger, snap *walpb.Snapshot) (*WAL, error) {
err := w.Close()
if err != nil {
lg.Panic("failed to close WAL during reopen", zap.Error(err))
@@ -324,7 +325,7 @@ func (w *WAL) renameWALUnlock(tmpdirpath string) (*WAL, error) {
}
// reopen and relock
- newWAL, oerr := Open(w.lg, w.dir, walpb.Snapshot{})
+ newWAL, oerr := Open(w.lg, w.dir, &walpb.Snapshot{})
if oerr != nil {
return nil, oerr
}
@@ -341,7 +342,7 @@ func (w *WAL) renameWALUnlock(tmpdirpath string) (*WAL, error) {
// The returned WAL is ready to read and the first record will be the one after
// the given snap. The WAL cannot be appended to before reading out all of its
// previous records.
-func Open(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error) {
+func Open(lg *zap.Logger, dirpath string, snap *walpb.Snapshot) (*WAL, error) {
w, err := openAtIndex(lg, dirpath, snap, true)
if err != nil {
return nil, fmt.Errorf("openAtIndex failed: %w", err)
@@ -354,14 +355,15 @@ func Open(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error) {
// OpenForRead only opens the wal files for read.
// Write on a read only wal panics.
-func OpenForRead(lg *zap.Logger, dirpath string, snap walpb.Snapshot) (*WAL, error) {
+func OpenForRead(lg *zap.Logger, dirpath string, snap *walpb.Snapshot) (*WAL, error) {
return openAtIndex(lg, dirpath, snap, false)
}
-func openAtIndex(lg *zap.Logger, dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) {
+func openAtIndex(lg *zap.Logger, dirpath string, startSnap *walpb.Snapshot, write bool) (*WAL, error) {
if lg == nil {
lg = zap.NewNop()
}
+ snap := startSnap.Clone()
names, nameIndex, err := selectWALFiles(lg, dirpath, snap)
if err != nil {
return nil, fmt.Errorf("[openAtIndex] selectWALFiles failed: %w", err)
@@ -396,15 +398,15 @@ func openAtIndex(lg *zap.Logger, dirpath string, snap walpb.Snapshot, write bool
return w, nil
}
-func selectWALFiles(lg *zap.Logger, dirpath string, snap walpb.Snapshot) ([]string, int, error) {
+func selectWALFiles(lg *zap.Logger, dirpath string, snap *walpb.Snapshot) ([]string, int, error) {
names, err := readWALNames(lg, dirpath)
if err != nil {
return nil, -1, fmt.Errorf("readWALNames failed: %w", err)
}
- nameIndex, ok := searchIndex(lg, names, snap.Index)
+ nameIndex, ok := searchIndex(lg, names, snap.GetIndex())
if !ok {
- return nil, -1, fmt.Errorf("wal: file not found which matches the snapshot index '%d'", snap.Index)
+ return nil, -1, fmt.Errorf("wal: file not found which matches the snapshot index '%d'", snap.GetIndex())
}
if !isValidSeq(lg, names[nameIndex:]) {
@@ -466,7 +468,8 @@ func openWALFiles(lg *zap.Logger, dirpath string, names []string, nameIndex int,
//
// ReadAll may return uncommitted yet entries, that are subject to be overridden.
// Do not apply entries that have index > state.commit, as they are subject to change.
-func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.Entry, err error) {
+func (w *WAL) ReadAll() (metadata []byte, state *raftpb.HardState, ents []*raftpb.Entry, err error) {
+ state = &raftpb.HardState{}
w.mu.Lock()
defer w.mu.Unlock()
@@ -479,24 +482,24 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
var match bool
for err = decoder.Decode(rec); err == nil; err = decoder.Decode(rec) {
- switch rec.Type {
+ switch rec.GetType() {
case EntryType:
e := MustUnmarshalEntry(rec.Data)
- // 0 <= e.Index-w.start.Index - 1 < len(ents)
- if e.Index > w.start.Index {
+ // 0 <= e.GetIndex()-w.start.GetIndex() - 1 < len(ents)
+ if e.GetIndex() > w.start.GetIndex() {
// prevent "panic: runtime error: slice bounds out of range [:13038096702221461992] with capacity 0"
- offset := e.Index - w.start.Index - 1
+ offset := e.GetIndex() - w.start.GetIndex() - 1
if offset > uint64(len(ents)) {
// return error before append call causes runtime panic.
// We still return the continuous WAL entries that have already been read.
// Refer to https://github.com/etcd-io/etcd/pull/19038#issuecomment-2557414292.
return nil, state, ents, fmt.Errorf("%w, snapshot[Index: %d, Term: %d], current entry[Index: %d, Term: %d], len(ents): %d",
- ErrSliceOutOfRange, w.start.Index, w.start.Term, e.Index, e.Term, len(ents))
+ ErrSliceOutOfRange, w.start.GetIndex(), w.start.GetTerm(), e.GetIndex(), e.GetTerm(), len(ents))
}
// The line below is potentially overriding some 'uncommitted' entries.
ents = append(ents[:offset], e)
}
- w.enti = e.Index
+ w.enti = e.GetIndex()
case StateType:
state = MustUnmarshalState(rec.Data)
@@ -516,13 +519,13 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
state.Reset()
return nil, state, nil, ErrCRCMismatch
}
- decoder.UpdateCRC(rec.Crc)
+ decoder.UpdateCRC(rec.GetCrc())
case SnapshotType:
var snap walpb.Snapshot
- pbutil.MustUnmarshal(&snap, rec.Data)
- if snap.Index == w.start.Index {
- if snap.Term != w.start.Term {
+ pbutil.MustUnmarshalMessage(&snap, rec.Data)
+ if snap.GetIndex() == w.start.GetIndex() {
+ if snap.GetTerm() != w.start.GetTerm() {
state.Reset()
return nil, state, nil, ErrSnapshotMismatch
}
@@ -574,7 +577,7 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
w.readClose()
w.readClose = nil
}
- w.start = walpb.Snapshot{}
+ w.start = &walpb.Snapshot{}
w.metadata = metadata
@@ -592,9 +595,9 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.
// ValidSnapshotEntries returns all the valid snapshot entries in the wal logs in the given directory.
// Snapshot entries are valid if their index is less than or equal to the most recent committed hardstate.
-func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]walpb.Snapshot, error) {
- var snaps []walpb.Snapshot
- var state raftpb.HardState
+func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]*walpb.Snapshot, error) {
+ var snaps []*walpb.Snapshot
+ state := &raftpb.HardState{}
var err error
rec := &walpb.Record{}
@@ -619,10 +622,10 @@ func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]walpb.Snapshot, erro
decoder := NewDecoder(rs...)
for err = decoder.Decode(rec); err == nil; err = decoder.Decode(rec) {
- switch rec.Type {
+ switch rec.GetType() {
case SnapshotType:
- var loadedSnap walpb.Snapshot
- pbutil.MustUnmarshal(&loadedSnap, rec.Data)
+ loadedSnap := &walpb.Snapshot{}
+ pbutil.MustUnmarshalMessage(loadedSnap, rec.Data)
snaps = append(snaps, loadedSnap)
case StateType:
state = MustUnmarshalState(rec.Data)
@@ -633,7 +636,7 @@ func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]walpb.Snapshot, erro
if crc != 0 && rec.Validate(crc) != nil {
return nil, ErrCRCMismatch
}
- decoder.UpdateCRC(rec.Crc)
+ decoder.UpdateCRC(rec.GetCrc())
}
}
// We do not have to read out all the WAL entries
@@ -645,7 +648,7 @@ func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]walpb.Snapshot, erro
// filter out any snaps that are newer than the committed hardstate
n := 0
for _, s := range snaps {
- if s.Index <= state.Commit {
+ if s.GetIndex() <= state.GetCommit() {
snaps[n] = s
n++
}
@@ -661,11 +664,11 @@ func ValidSnapshotEntries(lg *zap.Logger, walDir string) ([]walpb.Snapshot, erro
// If it cannot read out the expected snap, it will return ErrSnapshotNotFound.
// If the loaded snap doesn't match with the expected one, it will
// return error ErrSnapshotMismatch.
-func Verify(lg *zap.Logger, walDir string, snap walpb.Snapshot) (*raftpb.HardState, error) {
+func Verify(lg *zap.Logger, walDir string, snap *walpb.Snapshot) (*raftpb.HardState, error) {
var metadata []byte
var err error
var match bool
- var state raftpb.HardState
+ state := &raftpb.HardState{}
rec := &walpb.Record{}
@@ -693,7 +696,7 @@ func Verify(lg *zap.Logger, walDir string, snap walpb.Snapshot) (*raftpb.HardSta
decoder := NewDecoder(rs...)
for err = decoder.Decode(rec); err == nil; err = decoder.Decode(rec) {
- switch rec.Type {
+ switch rec.GetType() {
case MetadataType:
if metadata != nil && !bytes.Equal(metadata, rec.Data) {
return nil, ErrMetadataConflict
@@ -706,12 +709,12 @@ func Verify(lg *zap.Logger, walDir string, snap walpb.Snapshot) (*raftpb.HardSta
if crc != 0 && rec.Validate(crc) != nil {
return nil, ErrCRCMismatch
}
- decoder.UpdateCRC(rec.Crc)
+ decoder.UpdateCRC(rec.GetCrc())
case SnapshotType:
var loadedSnap walpb.Snapshot
- pbutil.MustUnmarshal(&loadedSnap, rec.Data)
- if loadedSnap.Index == snap.Index {
- if loadedSnap.Term != snap.Term {
+ pbutil.MustUnmarshalMessage(&loadedSnap, rec.Data)
+ if loadedSnap.GetIndex() == snap.GetIndex() {
+ if loadedSnap.GetTerm() != snap.GetTerm() {
return nil, ErrSnapshotMismatch
}
match = true
@@ -720,7 +723,7 @@ func Verify(lg *zap.Logger, walDir string, snap walpb.Snapshot) (*raftpb.HardSta
// are not necessary for validating the WAL contents
case EntryType:
case StateType:
- pbutil.MustUnmarshal(&state, rec.Data)
+ pbutil.MustUnmarshalMessage(state, rec.Data)
default:
return nil, fmt.Errorf("unexpected block type %d", rec.Type)
}
@@ -736,7 +739,7 @@ func Verify(lg *zap.Logger, walDir string, snap walpb.Snapshot) (*raftpb.HardSta
return nil, ErrSnapshotNotFound
}
- return &state, nil
+ return state, nil
}
// cut closes current file written and creates a new one ready to append.
@@ -777,11 +780,11 @@ func (w *WAL) cut() error {
return err
}
- if err = w.encoder.encode(&walpb.Record{Type: MetadataType, Data: w.metadata}); err != nil {
+ if err = w.encoder.encode(&walpb.Record{Type: new(MetadataType), Data: w.metadata}); err != nil {
return err
}
- if err = w.saveState(&w.state); err != nil {
+ if err = w.saveState(w.state); err != nil {
return err
}
@@ -933,26 +936,26 @@ func (w *WAL) Close() error {
func (w *WAL) saveEntry(e *raftpb.Entry) error {
// TODO: add MustMarshalTo to reduce one allocation.
- b := pbutil.MustMarshal(e)
- rec := &walpb.Record{Type: EntryType, Data: b}
+ b := pbutil.MustMarshalMessage(e)
+ rec := &walpb.Record{Type: new(EntryType), Data: b}
if err := w.encoder.encode(rec); err != nil {
return err
}
- w.enti = e.Index
+ w.enti = e.GetIndex()
return nil
}
func (w *WAL) saveState(s *raftpb.HardState) error {
- if raft.IsEmptyHardState(*s) {
+ if raft.IsEmptyHardState(s) {
return nil
}
- w.state = *s
- b := pbutil.MustMarshal(s)
- rec := &walpb.Record{Type: StateType, Data: b}
+ w.state = s
+ b := pbutil.MustMarshalMessage(s)
+ rec := &walpb.Record{Type: new(StateType), Data: b}
return w.encoder.encode(rec)
}
-func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
+func (w *WAL) Save(st *raftpb.HardState, ents []*raftpb.Entry) error {
w.mu.Lock()
defer w.mu.Unlock()
@@ -960,16 +963,22 @@ func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
if raft.IsEmptyHardState(st) && len(ents) == 0 {
return nil
}
+ if st == nil {
+ st = &raftpb.HardState{}
+ }
+ if w.state == nil {
+ w.state = &raftpb.HardState{}
+ }
mustSync := raft.MustSync(st, w.state, len(ents))
// TODO(xiangli): no more reference operator
for i := range ents {
- if err := w.saveEntry(&ents[i]); err != nil {
+ if err := w.saveEntry(ents[i]); err != nil {
return err
}
}
- if err := w.saveState(&st); err != nil {
+ if err := w.saveState(st); err != nil {
return err
}
@@ -990,29 +999,32 @@ func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
return w.cut()
}
-func (w *WAL) SaveSnapshot(e walpb.Snapshot) error {
- if err := walpb.ValidateSnapshotForWrite(&e); err != nil {
+func (w *WAL) SaveSnapshot(e *walpb.Snapshot) error {
+ if e == nil {
+ return errors.New("snapshot is nil")
+ }
+ if err := walpb.ValidateSnapshotForWrite(e); err != nil {
return err
}
- b := pbutil.MustMarshal(&e)
+ b := pbutil.MustMarshalMessage(e)
w.mu.Lock()
defer w.mu.Unlock()
- rec := &walpb.Record{Type: SnapshotType, Data: b}
+ rec := &walpb.Record{Type: new(SnapshotType), Data: b}
if err := w.encoder.encode(rec); err != nil {
return err
}
// update enti only when snapshot is ahead of last index
- if w.enti < e.Index {
- w.enti = e.Index
+ if w.enti < e.GetIndex() {
+ w.enti = e.GetIndex()
}
return w.sync()
}
func (w *WAL) saveCrc(prevCrc uint32) error {
- return w.encoder.encode(&walpb.Record{Type: CrcType, Crc: prevCrc})
+ return w.encoder.encode(&walpb.Record{Type: new(CrcType), Crc: &prevCrc})
}
func (w *WAL) tail() *fileutil.LockedFile {
diff --git a/server/storage/wal/wal_bench_test.go b/server/storage/wal/wal_bench_test.go
index 61c8d42fab6d..e1c3601f2d53 100644
--- a/server/storage/wal/wal_bench_test.go
+++ b/server/storage/wal/wal_bench_test.go
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/raft/v3/raftpb"
)
@@ -48,7 +49,7 @@ func benchmarkWriteEntry(b *testing.B, size int, batch int) {
b.ResetTimer()
n := 0
- b.SetBytes(int64(e.Size()))
+ b.SetBytes(int64(proto.Size(e)))
for i := 0; i < b.N; i++ {
err := w.saveEntry(e)
if err != nil {
diff --git a/server/storage/wal/wal_test.go b/server/storage/wal/wal_test.go
index bbc6f5181433..940b877993d9 100644
--- a/server/storage/wal/wal_test.go
+++ b/server/storage/wal/wal_test.go
@@ -24,14 +24,15 @@ import (
"os"
"path"
"path/filepath"
- "reflect"
"regexp"
"strings"
"testing"
+ "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/pkg/v3/pbutil"
@@ -41,7 +42,7 @@ import (
var confState = raftpb.ConfState{
Voters: []uint64{0x00ffca74},
- AutoLeave: false,
+ AutoLeave: new(bool(false)),
}
func TestNew(t *testing.T) {
@@ -69,15 +70,27 @@ func TestNew(t *testing.T) {
t.Fatalf("err = %v, want nil", err)
}
+ if os.Getenv("ETCD_UPDATE_FIXTURE_DATA") == "true" {
+ os.WriteFile("testdata/TestNew.wal", gd, os.FileMode(0644))
+ }
+ fixtureData, err := os.ReadFile("testdata/TestNew.wal")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(gd, fixtureData) {
+ t.Log("data did not match fixture, rerun with ETCD_UPDATE_FIXTURE_DATA=true to regenerate fixture")
+ t.Errorf("data = %v, want %v", gd, fixtureData)
+ }
+
var wb bytes.Buffer
e := newEncoder(&wb, 0, 0)
- err = e.encode(&walpb.Record{Type: CrcType, Crc: 0})
+ err = e.encode(&walpb.Record{Type: new(CrcType), Crc: new(uint32(0))})
require.NoErrorf(t, err, "err = %v, want nil", err)
- err = e.encode(&walpb.Record{Type: MetadataType, Data: []byte("somedata")})
+ err = e.encode(&walpb.Record{Type: new(MetadataType), Data: []byte("somedata")})
require.NoErrorf(t, err, "err = %v, want nil", err)
r := &walpb.Record{
- Type: SnapshotType,
- Data: pbutil.MustMarshal(&walpb.Snapshot{}),
+ Type: new(SnapshotType),
+ Data: pbutil.MustMarshalMessage(&walpb.Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}),
}
err = e.encode(r)
require.NoErrorf(t, err, "err = %v, want nil", err)
@@ -216,7 +229,7 @@ func TestOpenAtIndex(t *testing.T) {
}
f.Close()
- w, err := Open(zaptest.NewLogger(t), dir, walpb.Snapshot{})
+ w, err := Open(zaptest.NewLogger(t), dir, &walpb.Snapshot{})
require.NoErrorf(t, err, "err = %v, want nil", err)
if g := filepath.Base(w.tail().Name()); g != walName(0, 0) {
t.Errorf("name = %+v, want %+v", g, walName(0, 0))
@@ -233,7 +246,7 @@ func TestOpenAtIndex(t *testing.T) {
}
f.Close()
- w, err = Open(zaptest.NewLogger(t), dir, walpb.Snapshot{Index: 5})
+ w, err = Open(zaptest.NewLogger(t), dir, &walpb.Snapshot{Index: new(uint64(5))})
require.NoErrorf(t, err, "err = %v, want nil", err)
if g := filepath.Base(w.tail().Name()); g != wname {
t.Errorf("name = %+v, want %+v", g, wname)
@@ -244,7 +257,7 @@ func TestOpenAtIndex(t *testing.T) {
w.Close()
emptydir := t.TempDir()
- if _, err = Open(zaptest.NewLogger(t), emptydir, walpb.Snapshot{}); !errors.Is(err, ErrFileNotFound) {
+ if _, err = Open(zaptest.NewLogger(t), emptydir, &walpb.Snapshot{}); !errors.Is(err, ErrFileNotFound) {
t.Errorf("err = %v, want %v", err, ErrFileNotFound)
}
}
@@ -265,8 +278,8 @@ func TestVerify(t *testing.T) {
// make 5 separate files
for i := 0; i < 5; i++ {
- es := []raftpb.Entry{{Index: uint64(i), Data: []byte(fmt.Sprintf("waldata%d", i+1))}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i)), Data: []byte(fmt.Sprintf("waldata%d", i+1))}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = w.cut(); err != nil {
@@ -274,15 +287,17 @@ func TestVerify(t *testing.T) {
}
}
- hs := raftpb.HardState{Term: 1, Vote: 3, Commit: 5}
- require.NoError(t, w.Save(hs, nil))
+ hs := raftpb.HardState{Term: new(uint64(1)), Vote: new(uint64(3)), Commit: new(uint64(5))}
+ require.NoError(t, w.Save(&hs, nil))
// to verify the WAL is not corrupted at this point
- hardstate, err := Verify(lg, walDir, walpb.Snapshot{})
+ hardstate, err := Verify(lg, walDir, &walpb.Snapshot{})
if err != nil {
t.Errorf("expected a nil error, got %v", err)
}
- assert.Equal(t, hs, *hardstate)
+ if diff := cmp.Diff(&hs, hardstate, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected hardstate (-want +got):\n%s", diff)
+ }
walFiles, err := os.ReadDir(walDir)
if err != nil {
@@ -295,7 +310,7 @@ func TestVerify(t *testing.T) {
t.Fatal(err)
}
- _, err = Verify(lg, walDir, walpb.Snapshot{})
+ _, err = Verify(lg, walDir, &walpb.Snapshot{})
if err == nil {
t.Error("expected a non-nil error, got nil")
}
@@ -312,7 +327,7 @@ func TestCut(t *testing.T) {
}
defer w.Close()
- state := raftpb.HardState{Term: 1}
+ state := &raftpb.HardState{Term: new(uint64(1))}
if err = w.Save(state, nil); err != nil {
t.Fatal(err)
}
@@ -324,15 +339,15 @@ func TestCut(t *testing.T) {
t.Errorf("name = %s, want %s", g, wname)
}
- es := []raftpb.Entry{{Index: 1, Term: 1, Data: []byte{1}}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte{1}}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = w.cut(); err != nil {
t.Fatal(err)
}
- snap := walpb.Snapshot{Index: 2, Term: 1, ConfState: &confState}
- if err = w.SaveSnapshot(snap); err != nil {
+ snap := walpb.Snapshot{Index: new(uint64(2)), Term: new(uint64(1)), ConfState: &confState}
+ if err = w.SaveSnapshot(&snap); err != nil {
t.Fatal(err)
}
wname = walName(2, 2)
@@ -350,14 +365,14 @@ func TestCut(t *testing.T) {
defer f.Close()
nw := &WAL{
decoder: NewDecoder(fileutil.NewFileReader(f)),
- start: snap,
+ start: &snap,
}
_, gst, _, err := nw.ReadAll()
if err != nil {
t.Fatal(err)
}
- if !reflect.DeepEqual(gst, state) {
- t.Errorf("state = %+v, want %+v", gst, state)
+ if diff := cmp.Diff(state, gst, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected state (-want +got):\n%s", diff)
}
}
@@ -369,7 +384,7 @@ func TestSaveWithCut(t *testing.T) {
t.Fatal(err)
}
- state := raftpb.HardState{Term: 1}
+ state := &raftpb.HardState{Term: new(uint64(1))}
if err = w.Save(state, nil); err != nil {
t.Fatal(err)
}
@@ -383,7 +398,7 @@ func TestSaveWithCut(t *testing.T) {
defer func() { SegmentSizeBytes = restoreLater }()
index := uint64(0)
for totalSize := 0; totalSize < int(SegmentSizeBytes); totalSize += EntrySize {
- ents := []raftpb.Entry{{Index: index, Term: 1, Data: bigData}}
+ ents := []*raftpb.Entry{{Index: new(index), Term: new(uint64(1)), Data: bigData}}
if err = w.Save(state, ents); err != nil {
t.Fatal(err)
}
@@ -392,7 +407,7 @@ func TestSaveWithCut(t *testing.T) {
w.Close()
- neww, err := Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ neww, err := Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
require.NoErrorf(t, err, "err = %v, want nil", err)
defer neww.Close()
wname := walName(1, index)
@@ -405,8 +420,8 @@ func TestSaveWithCut(t *testing.T) {
t.Fatal(err)
}
- if !reflect.DeepEqual(newhardstate, state) {
- t.Errorf("Hard State = %+v, want %+v", newhardstate, state)
+ if diff := cmp.Diff(state, newhardstate, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected Hard State (-want +got):\n%s", diff)
}
if len(entries) != int(SegmentSizeBytes/int64(EntrySize)) {
t.Errorf("Number of entries = %d, expected = %d", len(entries), int(SegmentSizeBytes/int64(EntrySize)))
@@ -445,7 +460,7 @@ func TestRecover(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ if err = w.SaveSnapshot(&walpb.Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}); err != nil {
t.Fatal(err)
}
@@ -456,11 +471,17 @@ func TestRecover(t *testing.T) {
t.Errorf("Unexpected error: %v", err)
}
- ents := []raftpb.Entry{{Index: 1, Term: 1, Data: data}, {Index: 2, Term: 2, Data: data}}
- if err = w.Save(raftpb.HardState{}, ents); err != nil {
+ ents := []*raftpb.Entry{
+ {Index: new(uint64(1)), Term: new(uint64(1)), Data: data},
+ {Index: new(uint64(2)), Term: new(uint64(2)), Data: data},
+ }
+ if err = w.Save(&raftpb.HardState{}, ents); err != nil {
t.Fatal(err)
}
- sts := []raftpb.HardState{{Term: 1, Vote: 1, Commit: 1}, {Term: 2, Vote: 2, Commit: 2}}
+ sts := []*raftpb.HardState{
+ {Term: new(uint64(1)), Vote: new(uint64(1)), Commit: new(uint64(1))},
+ {Term: new(uint64(2)), Vote: new(uint64(2)), Commit: new(uint64(2))},
+ }
for _, s := range sts {
if err = w.Save(s, nil); err != nil {
t.Fatal(err)
@@ -468,7 +489,7 @@ func TestRecover(t *testing.T) {
}
w.Close()
- if w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{}); err != nil {
+ if w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{}); err != nil {
t.Fatal(err)
}
metadata, state, entries, err := w.ReadAll()
@@ -479,13 +500,13 @@ func TestRecover(t *testing.T) {
if !bytes.Equal(metadata, []byte("metadata")) {
t.Errorf("metadata = %s, want %s", metadata, "metadata")
}
- if !reflect.DeepEqual(entries, ents) {
- t.Errorf("ents = %+v, want %+v", entries, ents)
+ if diff := cmp.Diff(ents, entries, protocmp.Transform(), cmp.Comparer(bytes.Equal)); diff != "" {
+ t.Errorf("unexpected entries (-want +got):\n%s", diff)
}
// only the latest state is recorded
s := sts[len(sts)-1]
- if !reflect.DeepEqual(state, s) {
- t.Errorf("state = %+v, want %+v", state, s)
+ if diff := cmp.Diff(s, state, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected state (-want +got):\n%s", diff)
}
w.Close()
})
@@ -567,11 +588,11 @@ func TestRecoverAfterCut(t *testing.T) {
t.Fatal(err)
}
for i := 0; i < 10; i++ {
- if err = md.SaveSnapshot(walpb.Snapshot{Index: uint64(i), Term: 1, ConfState: &confState}); err != nil {
+ if err = md.SaveSnapshot(&walpb.Snapshot{Index: new(uint64(i)), Term: new(uint64(1)), ConfState: &confState}); err != nil {
t.Fatal(err)
}
- es := []raftpb.Entry{{Index: uint64(i)}}
- if err = md.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i))}}
+ if err = md.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = md.cut(); err != nil {
@@ -585,7 +606,7 @@ func TestRecoverAfterCut(t *testing.T) {
}
for i := 0; i < 10; i++ {
- w, err := Open(zaptest.NewLogger(t), p, walpb.Snapshot{Index: uint64(i), Term: 1})
+ w, err := Open(zaptest.NewLogger(t), p, &walpb.Snapshot{Index: new(uint64(i)), Term: new(uint64(1))})
if err != nil {
if i <= 4 {
if !strings.Contains(err.Error(), "do not increase continuously") {
@@ -605,8 +626,8 @@ func TestRecoverAfterCut(t *testing.T) {
t.Errorf("#%d: metadata = %s, want %s", i, metadata, "metadata")
}
for j, e := range entries {
- if e.Index != uint64(j+i+1) {
- t.Errorf("#%d: ents[%d].Index = %+v, want %+v", i, j, e.Index, j+i+1)
+ if e.GetIndex() != uint64(j+i+1) {
+ t.Errorf("#%d: ents[%d].Index = %+v, want %+v", i, j, e.GetIndex(), j+i+1)
}
}
w.Close()
@@ -620,15 +641,15 @@ func TestOpenAtUncommittedIndex(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ if err = w.SaveSnapshot(&walpb.Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}); err != nil {
t.Fatal(err)
}
- if err = w.Save(raftpb.HardState{}, []raftpb.Entry{{Index: 0}}); err != nil {
+ if err = w.Save(&raftpb.HardState{}, []*raftpb.Entry{{Index: new(uint64(0))}}); err != nil {
t.Fatal(err)
}
w.Close()
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -653,8 +674,8 @@ func TestOpenForRead(t *testing.T) {
defer w.Close()
// make 10 separate files
for i := 0; i < 10; i++ {
- es := []raftpb.Entry{{Index: uint64(i)}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i))}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = w.cut(); err != nil {
@@ -666,14 +687,14 @@ func TestOpenForRead(t *testing.T) {
w.ReleaseLockTo(unlockIndex)
// All are available for read
- w2, err := OpenForRead(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w2, err := OpenForRead(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
defer w2.Close()
_, _, ents, err := w2.ReadAll()
require.NoErrorf(t, err, "err = %v, want nil", err)
- if g := ents[len(ents)-1].Index; g != 9 {
+ if g := ents[len(ents)-1].GetIndex(); g != 9 {
t.Errorf("last index read = %d, want %d", g, 9)
}
}
@@ -691,14 +712,14 @@ func TestOpenWithMaxIndex(t *testing.T) {
}
}()
- es := []raftpb.Entry{{Index: uint64(math.MaxInt64)}}
- if err = w1.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(math.MaxInt64))}}
+ if err = w1.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
w1.Close()
w1 = nil
- w2, err := Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w2, err := Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -743,8 +764,8 @@ func TestReleaseLockTo(t *testing.T) {
// make 10 separate files
for i := 0; i < 10; i++ {
- es := []raftpb.Entry{{Index: uint64(i)}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i))}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if err = w.cut(); err != nil {
@@ -800,8 +821,8 @@ func TestTailWriteNoSlackSpace(t *testing.T) {
}
// write some entries
for i := 1; i <= 5; i++ {
- es := []raftpb.Entry{{Index: uint64(i), Term: 1, Data: []byte{byte(i)}}}
- if err = w.Save(raftpb.HardState{Term: 1}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i)), Term: new(uint64(1)), Data: []byte{byte(i)}}}
+ if err = w.Save(&raftpb.HardState{Term: new(uint64(1))}, es); err != nil {
t.Fatal(err)
}
}
@@ -816,7 +837,7 @@ func TestTailWriteNoSlackSpace(t *testing.T) {
w.Close()
// open, write more
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -827,15 +848,15 @@ func TestTailWriteNoSlackSpace(t *testing.T) {
require.Lenf(t, ents, 5, "got entries %+v, expected 5 entries", ents)
// write more entries
for i := 6; i <= 10; i++ {
- es := []raftpb.Entry{{Index: uint64(i), Term: 1, Data: []byte{byte(i)}}}
- if err = w.Save(raftpb.HardState{Term: 1}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i)), Term: new(uint64(1)), Data: []byte{byte(i)}}}
+ if err = w.Save(&raftpb.HardState{Term: new(uint64(1))}, es); err != nil {
t.Fatal(err)
}
}
w.Close()
// confirm all writes
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -872,7 +893,7 @@ func TestRestartCreateWal(t *testing.T) {
t.Fatalf("got %q exists, expected it to not exist", tmpdir)
}
- if w, err = OpenForRead(zaptest.NewLogger(t), p, walpb.Snapshot{}); err != nil {
+ if w, err = OpenForRead(zaptest.NewLogger(t), p, &walpb.Snapshot{}); err != nil {
t.Fatal(err)
}
defer w.Close()
@@ -902,8 +923,8 @@ func TestOpenOnTornWrite(t *testing.T) {
// get offset of end of each saved entry
offsets := make([]int64, maxEntries)
for i := range offsets {
- es := []raftpb.Entry{{Index: uint64(i)}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i))}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
if offsets[i], err = w.tail().Seek(0, io.SeekCurrent); err != nil {
@@ -931,7 +952,7 @@ func TestOpenOnTornWrite(t *testing.T) {
}
f.Close()
- w, err = Open(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = Open(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -944,15 +965,15 @@ func TestOpenOnTornWrite(t *testing.T) {
// write a few entries past the clobbered entry
for i := 0; i < overwriteEntries; i++ {
// Index is different from old, truncated entries
- es := []raftpb.Entry{{Index: uint64(i + clobberIdx), Data: []byte("new")}}
- if err = w.Save(raftpb.HardState{}, es); err != nil {
+ es := []*raftpb.Entry{{Index: new(uint64(i + clobberIdx)), Data: []byte("new")}}
+ if err = w.Save(&raftpb.HardState{}, es); err != nil {
t.Fatal(err)
}
}
w.Close()
// read back the entries, confirm number of entries matches expectation
- w, err = OpenForRead(zaptest.NewLogger(t), p, walpb.Snapshot{})
+ w, err = OpenForRead(zaptest.NewLogger(t), p, &walpb.Snapshot{})
if err != nil {
t.Fatal(err)
}
@@ -1009,13 +1030,13 @@ func TestReadAllFail(t *testing.T) {
// for hardstate
func TestValidSnapshotEntries(t *testing.T) {
p := t.TempDir()
- snap0 := walpb.Snapshot{}
- snap1 := walpb.Snapshot{Index: 1, Term: 1, ConfState: &confState}
- state1 := raftpb.HardState{Commit: 1, Term: 1}
- snap2 := walpb.Snapshot{Index: 2, Term: 1, ConfState: &confState}
- snap3 := walpb.Snapshot{Index: 3, Term: 2, ConfState: &confState}
- state2 := raftpb.HardState{Commit: 3, Term: 2}
- snap4 := walpb.Snapshot{Index: 4, Term: 2, ConfState: &confState} // will be orphaned since the last committed entry will be snap3
+ snap0 := walpb.Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}
+ snap1 := walpb.Snapshot{Index: new(uint64(1)), Term: new(uint64(1)), ConfState: &confState}
+ state1 := raftpb.HardState{Commit: new(uint64(1)), Term: new(uint64(1))}
+ snap2 := walpb.Snapshot{Index: new(uint64(2)), Term: new(uint64(1)), ConfState: &confState}
+ snap3 := walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(2)), ConfState: &confState}
+ state2 := raftpb.HardState{Commit: new(uint64(3)), Term: new(uint64(2))}
+ snap4 := walpb.Snapshot{Index: new(uint64(4)), Term: new(uint64(2)), ConfState: &confState} // will be orphaned since the last committed entry will be snap3
func() {
w, err := Create(zaptest.NewLogger(t), p, nil)
if err != nil {
@@ -1024,22 +1045,22 @@ func TestValidSnapshotEntries(t *testing.T) {
defer w.Close()
// snap0 is implicitly created at index 0, term 0
- if err = w.SaveSnapshot(snap1); err != nil {
+ if err = w.SaveSnapshot(&snap1); err != nil {
t.Fatal(err)
}
- if err = w.Save(state1, nil); err != nil {
+ if err = w.Save(&state1, nil); err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(snap2); err != nil {
+ if err = w.SaveSnapshot(&snap2); err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(snap3); err != nil {
+ if err = w.SaveSnapshot(&snap3); err != nil {
t.Fatal(err)
}
- if err = w.Save(state2, nil); err != nil {
+ if err = w.Save(&state2, nil); err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(snap4); err != nil {
+ if err = w.SaveSnapshot(&snap4); err != nil {
t.Fatal(err)
}
}()
@@ -1047,9 +1068,9 @@ func TestValidSnapshotEntries(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- expected := []walpb.Snapshot{snap0, snap1, snap2, snap3}
- if !reflect.DeepEqual(walSnaps, expected) {
- t.Errorf("expected walSnaps %+v, got %+v", expected, walSnaps)
+ expected := []*walpb.Snapshot{&snap0, &snap1, &snap2, &snap3}
+ if diff := cmp.Diff(expected, walSnaps, protocmp.Transform()); diff != "" {
+ t.Errorf("walSnaps mismatch (-want +got):\n%s", diff)
}
}
@@ -1063,11 +1084,11 @@ func TestValidSnapshotEntriesAfterPurgeWal(t *testing.T) {
}()
p := t.TempDir()
snap0 := walpb.Snapshot{}
- snap1 := walpb.Snapshot{Index: 1, Term: 1, ConfState: &confState}
- state1 := raftpb.HardState{Commit: 1, Term: 1}
- snap2 := walpb.Snapshot{Index: 2, Term: 1, ConfState: &confState}
- snap3 := walpb.Snapshot{Index: 3, Term: 2, ConfState: &confState}
- state2 := raftpb.HardState{Commit: 3, Term: 2}
+ snap1 := walpb.Snapshot{Index: new(uint64(1)), Term: new(uint64(1)), ConfState: &confState}
+ state1 := raftpb.HardState{Commit: new(uint64(1)), Term: new(uint64(1))}
+ snap2 := walpb.Snapshot{Index: new(uint64(2)), Term: new(uint64(1)), ConfState: &confState}
+ snap3 := walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(2)), ConfState: &confState}
+ state2 := raftpb.HardState{Commit: new(uint64(3)), Term: new(uint64(2))}
func() {
w, err := Create(zaptest.NewLogger(t), p, nil)
if err != nil {
@@ -1076,25 +1097,25 @@ func TestValidSnapshotEntriesAfterPurgeWal(t *testing.T) {
defer w.Close()
// snap0 is implicitly created at index 0, term 0
- if err = w.SaveSnapshot(snap1); err != nil {
+ if err = w.SaveSnapshot(&snap1); err != nil {
t.Fatal(err)
}
- if err = w.Save(state1, nil); err != nil {
+ if err = w.Save(&state1, nil); err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(snap2); err != nil {
+ if err = w.SaveSnapshot(&snap2); err != nil {
t.Fatal(err)
}
- if err = w.SaveSnapshot(snap3); err != nil {
+ if err = w.SaveSnapshot(&snap3); err != nil {
t.Fatal(err)
}
for i := 0; i < 128; i++ {
- if err = w.Save(state2, nil); err != nil {
+ if err = w.Save(&state2, nil); err != nil {
t.Fatal(err)
}
}
}()
- files, _, err := selectWALFiles(nil, p, snap0)
+ files, _, err := selectWALFiles(nil, p, &snap0)
if err != nil {
t.Fatal(err)
}
@@ -1111,8 +1132,8 @@ func TestLastRecordLengthExceedFileEnd(t *testing.T) {
* sure it exceeds the end of the file.
*
* for i := 0; i < 3; i++ {
- * es := []raftpb.Entry{{Index: uint64(i + 1), Data: []byte(fmt.Sprintf("waldata%d", i+1))}}
- * if err = w.Save(raftpb.HardState{}, es); err != nil {
+ * es := []*raftpb.Entry{{Index: uint64(i + 1), Data: []byte(fmt.Sprintf("waldata%d", i+1))}}
+ * if err = w.Save(&raftpb.HardState{}, es); err != nil {
* t.Fatal(err)
* }
* }
@@ -1152,11 +1173,11 @@ func TestLastRecordLengthExceedFileEnd(t *testing.T) {
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
break
}
- if rec.Type == EntryType {
+ if rec.GetType() == EntryType {
e := MustUnmarshalEntry(rec.Data)
t.Logf("Validating normal entry: %v", e)
- recData := fmt.Sprintf("waldata%d", e.Index)
- require.Equal(t, raftpb.EntryNormal, e.Type)
+ recData := fmt.Sprintf("waldata%d", e.GetIndex())
+ require.Equal(t, raftpb.EntryNormal, e.GetType())
require.Equal(t, recData, string(e.Data))
}
rec = &walpb.Record{}
@@ -1168,9 +1189,9 @@ func TestLastRecordLengthExceedFileEnd(t *testing.T) {
newFileName := filepath.Join(filepath.Dir(fileName), "0000000000000000-0000000000000000.wal")
require.NoError(t, os.Rename(fileName, newFileName))
- w, err := Open(zaptest.NewLogger(t), filepath.Dir(fileName), walpb.Snapshot{
- Index: 0,
- Term: 0,
+ w, err := Open(zaptest.NewLogger(t), filepath.Dir(fileName), &walpb.Snapshot{
+ Index: new(uint64(0)),
+ Term: new(uint64(0)),
})
require.NoError(t, err)
defer w.Close()
diff --git a/server/storage/wal/walpb/record.go b/server/storage/wal/walpb/record.go
index 30e6c061366f..695e2633ee5f 100644
--- a/server/storage/wal/walpb/record.go
+++ b/server/storage/wal/walpb/record.go
@@ -17,15 +17,25 @@ package walpb
import (
"errors"
"fmt"
+
+ "google.golang.org/protobuf/proto"
)
var ErrCRCMismatch = errors.New("walpb: crc mismatch")
func (rec *Record) Validate(crc uint32) error {
- if rec.Crc == crc {
+ if rec.GetCrc() == crc {
return nil
}
- return fmt.Errorf("%w: expected: %x computed: %x", ErrCRCMismatch, rec.Crc, crc)
+ return fmt.Errorf("%w: expected: %x computed: %x", ErrCRCMismatch, rec.GetCrc(), crc)
+}
+
+// Clone returns a deep copy of s, or an empty Snapshot if s is nil.
+func (s *Snapshot) Clone() *Snapshot {
+ if s == nil {
+ return &Snapshot{}
+ }
+ return proto.Clone(s).(*Snapshot)
}
// ValidateSnapshotForWrite ensures the Snapshot the newly written snapshot is valid.
@@ -33,8 +43,14 @@ func (rec *Record) Validate(crc uint32) error {
// There might exist log-entries written by old etcd versions that does not conform
// to the requirements.
func ValidateSnapshotForWrite(e *Snapshot) error {
+ if e.Index == nil {
+ return errors.New("snapshot is missing index: " + e.String())
+ }
+ if e.Term == nil {
+ return errors.New("snapshot is missing term: " + e.String())
+ }
// Since etcd>=3.5.0
- if e.ConfState == nil && e.Index > 0 {
+ if e.ConfState == nil && e.GetIndex() > 0 {
return errors.New("Saved (not-initial) snapshot is missing ConfState: " + e.String())
}
return nil
diff --git a/server/storage/wal/walpb/record.pb.go b/server/storage/wal/walpb/record.pb.go
index 5605fbdaa310..00bf487ad7c1 100644
--- a/server/storage/wal/walpb/record.pb.go
+++ b/server/storage/wal/walpb/record.pb.go
@@ -1,611 +1,212 @@
-// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.11
+// protoc v3.20.3
// source: record.proto
package walpb
import (
- fmt "fmt"
- io "io"
- math "math"
- math_bits "math/bits"
+ reflect "reflect"
+ sync "sync"
+ unsafe "unsafe"
- _ "github.com/gogo/protobuf/gogoproto"
- proto "github.com/golang/protobuf/proto"
raftpb "go.etcd.io/raft/v3/raftpb"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
type Record struct {
- Type int64 `protobuf:"varint,1,opt,name=type" json:"type"`
- Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"`
- Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Type *int64 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"`
+ Crc *uint32 `protobuf:"varint,2,opt,name=crc" json:"crc,omitempty"`
+ Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Record) Reset() { *m = Record{} }
-func (m *Record) String() string { return proto.CompactTextString(m) }
-func (*Record) ProtoMessage() {}
-func (*Record) Descriptor() ([]byte, []int) {
- return fileDescriptor_bf94fd919e302a1d, []int{0}
-}
-func (m *Record) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Record.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
- }
- return b[:n], nil
- }
-}
-func (m *Record) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Record.Merge(m, src)
-}
-func (m *Record) XXX_Size() int {
- return m.Size()
+func (x *Record) Reset() {
+ *x = Record{}
+ mi := &file_record_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Record) XXX_DiscardUnknown() {
- xxx_messageInfo_Record.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Record proto.InternalMessageInfo
-// Keep in sync with raftpb.SnapshotMetadata.
-type Snapshot struct {
- Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"`
- Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"`
- // Field populated since >=etcd-3.5.0.
- ConfState *raftpb.ConfState `protobuf:"bytes,3,opt,name=conf_state,json=confState" json:"conf_state,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+func (x *Record) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Snapshot) Reset() { *m = Snapshot{} }
-func (m *Snapshot) String() string { return proto.CompactTextString(m) }
-func (*Snapshot) ProtoMessage() {}
-func (*Snapshot) Descriptor() ([]byte, []int) {
- return fileDescriptor_bf94fd919e302a1d, []int{1}
-}
-func (m *Snapshot) XXX_Unmarshal(b []byte) error {
- return m.Unmarshal(b)
-}
-func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
- if deterministic {
- return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic)
- } else {
- b = b[:cap(b)]
- n, err := m.MarshalToSizedBuffer(b)
- if err != nil {
- return nil, err
+func (*Record) ProtoMessage() {}
+
+func (x *Record) ProtoReflect() protoreflect.Message {
+ mi := &file_record_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- return b[:n], nil
+ return ms
}
+ return mi.MessageOf(x)
}
-func (m *Snapshot) XXX_Merge(src proto.Message) {
- xxx_messageInfo_Snapshot.Merge(m, src)
-}
-func (m *Snapshot) XXX_Size() int {
- return m.Size()
-}
-func (m *Snapshot) XXX_DiscardUnknown() {
- xxx_messageInfo_Snapshot.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Snapshot proto.InternalMessageInfo
-func init() {
- proto.RegisterType((*Record)(nil), "walpb.Record")
- proto.RegisterType((*Snapshot)(nil), "walpb.Snapshot")
+// Deprecated: Use Record.ProtoReflect.Descriptor instead.
+func (*Record) Descriptor() ([]byte, []int) {
+ return file_record_proto_rawDescGZIP(), []int{0}
}
-func init() { proto.RegisterFile("record.proto", fileDescriptor_bf94fd919e302a1d) }
-
-var fileDescriptor_bf94fd919e302a1d = []byte{
- // 266 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x90, 0x41, 0x4e, 0xc3, 0x30,
- 0x14, 0x44, 0x63, 0x92, 0x22, 0x30, 0x65, 0x51, 0x0b, 0xa1, 0x28, 0x8b, 0x10, 0x75, 0x15, 0x09,
- 0x29, 0x46, 0x70, 0x02, 0xca, 0x9e, 0x45, 0xba, 0x63, 0x83, 0x5c, 0xe7, 0x27, 0x54, 0x6a, 0xf3,
- 0xad, 0x1f, 0xab, 0x85, 0x9b, 0x70, 0xa4, 0x2c, 0x39, 0x01, 0x82, 0x70, 0x11, 0x64, 0xa7, 0xb0,
- 0xfa, 0xa3, 0x37, 0x9a, 0x19, 0xcb, 0x7c, 0x4a, 0xa0, 0x91, 0xaa, 0xc2, 0x10, 0x5a, 0x14, 0x93,
- 0xbd, 0xda, 0x98, 0x55, 0x72, 0xd1, 0x60, 0x83, 0x9e, 0x48, 0xa7, 0x46, 0x33, 0x99, 0x91, 0xaa,
- 0xad, 0x59, 0x49, 0x77, 0x46, 0x34, 0x7f, 0xe4, 0xc7, 0xa5, 0xcf, 0x8b, 0x98, 0x47, 0xf6, 0xcd,
- 0x40, 0xcc, 0x32, 0x96, 0x87, 0x8b, 0xa8, 0xff, 0xbc, 0x0a, 0x4a, 0x4f, 0xc4, 0x25, 0x0f, 0x35,
- 0xe9, 0xf8, 0x28, 0x63, 0xf9, 0xf9, 0xc1, 0x70, 0x40, 0x08, 0x1e, 0x55, 0xca, 0xaa, 0x38, 0xcc,
- 0x58, 0x3e, 0x2d, 0xbd, 0x9e, 0x13, 0x3f, 0x59, 0xb6, 0xca, 0x74, 0x2f, 0x68, 0x45, 0xc2, 0x27,
- 0xeb, 0xb6, 0x82, 0x57, 0x5f, 0x19, 0x1d, 0x92, 0x23, 0xf2, 0x6b, 0x40, 0x5b, 0x5f, 0x1a, 0xfd,
- 0xaf, 0x01, 0x6d, 0xc5, 0x0d, 0xe7, 0x1a, 0xdb, 0xfa, 0xb9, 0xb3, 0xca, 0x82, 0xef, 0x3e, 0xbb,
- 0x9d, 0x15, 0xe3, 0xcb, 0x8b, 0x07, 0x6c, 0xeb, 0xa5, 0x33, 0xca, 0x53, 0xfd, 0x27, 0x17, 0xf7,
- 0xfd, 0x77, 0x1a, 0xf4, 0x43, 0xca, 0x3e, 0x86, 0x94, 0x7d, 0x0d, 0x29, 0x7b, 0xff, 0x49, 0x83,
- 0xa7, 0xeb, 0x06, 0x0b, 0xb0, 0xba, 0x2a, 0xd6, 0x28, 0xdd, 0x95, 0x1d, 0xd0, 0x0e, 0x48, 0xee,
- 0xee, 0x64, 0x67, 0x91, 0x54, 0x03, 0x72, 0xaf, 0x36, 0xd2, 0xff, 0xd7, 0x6f, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xcf, 0xa9, 0xf0, 0x02, 0x45, 0x01, 0x00, 0x00,
+func (x *Record) GetType() int64 {
+ if x != nil && x.Type != nil {
+ return *x.Type
+ }
+ return 0
}
-func (m *Record) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
+func (x *Record) GetCrc() uint32 {
+ if x != nil && x.Crc != nil {
+ return *x.Crc
}
- return dAtA[:n], nil
+ return 0
}
-func (m *Record) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *Record) GetData() []byte {
+ if x != nil {
+ return x.Data
+ }
+ return nil
}
-func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.Data != nil {
- i -= len(m.Data)
- copy(dAtA[i:], m.Data)
- i = encodeVarintRecord(dAtA, i, uint64(len(m.Data)))
- i--
- dAtA[i] = 0x1a
- }
- i = encodeVarintRecord(dAtA, i, uint64(m.Crc))
- i--
- dAtA[i] = 0x10
- i = encodeVarintRecord(dAtA, i, uint64(m.Type))
- i--
- dAtA[i] = 0x8
- return len(dAtA) - i, nil
+// Keep in sync with raftpb.SnapshotMetadata.
+type Snapshot struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Index *uint64 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"`
+ Term *uint64 `protobuf:"varint,2,opt,name=term" json:"term,omitempty"`
+ // Field populated since >=etcd-3.5.0.
+ ConfState *raftpb.ConfState `protobuf:"bytes,3,opt,name=conf_state,json=confState" json:"conf_state,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (m *Snapshot) Marshal() (dAtA []byte, err error) {
- size := m.Size()
- dAtA = make([]byte, size)
- n, err := m.MarshalToSizedBuffer(dAtA[:size])
- if err != nil {
- return nil, err
- }
- return dAtA[:n], nil
+func (x *Snapshot) Reset() {
+ *x = Snapshot{}
+ mi := &file_record_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) {
- size := m.Size()
- return m.MarshalToSizedBuffer(dAtA[:size])
+func (x *Snapshot) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) {
- i := len(dAtA)
- _ = i
- var l int
- _ = l
- if m.XXX_unrecognized != nil {
- i -= len(m.XXX_unrecognized)
- copy(dAtA[i:], m.XXX_unrecognized)
- }
- if m.ConfState != nil {
- {
- size, err := m.ConfState.MarshalToSizedBuffer(dAtA[:i])
- if err != nil {
- return 0, err
- }
- i -= size
- i = encodeVarintRecord(dAtA, i, uint64(size))
+func (*Snapshot) ProtoMessage() {}
+
+func (x *Snapshot) ProtoReflect() protoreflect.Message {
+ mi := &file_record_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
}
- i--
- dAtA[i] = 0x1a
+ return ms
}
- i = encodeVarintRecord(dAtA, i, uint64(m.Term))
- i--
- dAtA[i] = 0x10
- i = encodeVarintRecord(dAtA, i, uint64(m.Index))
- i--
- dAtA[i] = 0x8
- return len(dAtA) - i, nil
+ return mi.MessageOf(x)
}
-func encodeVarintRecord(dAtA []byte, offset int, v uint64) int {
- offset -= sovRecord(v)
- base := offset
- for v >= 1<<7 {
- dAtA[offset] = uint8(v&0x7f | 0x80)
- v >>= 7
- offset++
- }
- dAtA[offset] = uint8(v)
- return base
-}
-func (m *Record) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRecord(uint64(m.Type))
- n += 1 + sovRecord(uint64(m.Crc))
- if m.Data != nil {
- l = len(m.Data)
- n += 1 + l + sovRecord(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
- }
- return n
+// Deprecated: Use Snapshot.ProtoReflect.Descriptor instead.
+func (*Snapshot) Descriptor() ([]byte, []int) {
+ return file_record_proto_rawDescGZIP(), []int{1}
}
-func (m *Snapshot) Size() (n int) {
- if m == nil {
- return 0
- }
- var l int
- _ = l
- n += 1 + sovRecord(uint64(m.Index))
- n += 1 + sovRecord(uint64(m.Term))
- if m.ConfState != nil {
- l = m.ConfState.Size()
- n += 1 + l + sovRecord(uint64(l))
- }
- if m.XXX_unrecognized != nil {
- n += len(m.XXX_unrecognized)
+func (x *Snapshot) GetIndex() uint64 {
+ if x != nil && x.Index != nil {
+ return *x.Index
}
- return n
+ return 0
}
-func sovRecord(x uint64) (n int) {
- return (math_bits.Len64(x|1) + 6) / 7
-}
-func sozRecord(x uint64) (n int) {
- return sovRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
-}
-func (m *Record) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Record: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Record: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
- }
- m.Type = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Type |= int64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
- }
- m.Crc = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Crc |= uint32(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
- }
- var byteLen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- byteLen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if byteLen < 0 {
- return ErrInvalidLengthRecord
- }
- postIndex := iNdEx + byteLen
- if postIndex < 0 {
- return ErrInvalidLengthRecord
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...)
- if m.Data == nil {
- m.Data = []byte{}
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRecord(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRecord
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
-
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Snapshot) GetTerm() uint64 {
+ if x != nil && x.Term != nil {
+ return *x.Term
}
- return nil
+ return 0
}
-func (m *Snapshot) Unmarshal(dAtA []byte) error {
- l := len(dAtA)
- iNdEx := 0
- for iNdEx < l {
- preIndex := iNdEx
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- fieldNum := int32(wire >> 3)
- wireType := int(wire & 0x7)
- if wireType == 4 {
- return fmt.Errorf("proto: Snapshot: wiretype end group for non-group")
- }
- if fieldNum <= 0 {
- return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire)
- }
- switch fieldNum {
- case 1:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
- }
- m.Index = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Index |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 2:
- if wireType != 0 {
- return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
- }
- m.Term = 0
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- m.Term |= uint64(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- case 3:
- if wireType != 2 {
- return fmt.Errorf("proto: wrong wireType = %d for field ConfState", wireType)
- }
- var msglen int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- msglen |= int(b&0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if msglen < 0 {
- return ErrInvalidLengthRecord
- }
- postIndex := iNdEx + msglen
- if postIndex < 0 {
- return ErrInvalidLengthRecord
- }
- if postIndex > l {
- return io.ErrUnexpectedEOF
- }
- if m.ConfState == nil {
- m.ConfState = &raftpb.ConfState{}
- }
- if err := m.ConfState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
- return err
- }
- iNdEx = postIndex
- default:
- iNdEx = preIndex
- skippy, err := skipRecord(dAtA[iNdEx:])
- if err != nil {
- return err
- }
- if (skippy < 0) || (iNdEx+skippy) < 0 {
- return ErrInvalidLengthRecord
- }
- if (iNdEx + skippy) > l {
- return io.ErrUnexpectedEOF
- }
- m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
- iNdEx += skippy
- }
- }
- if iNdEx > l {
- return io.ErrUnexpectedEOF
+func (x *Snapshot) GetConfState() *raftpb.ConfState {
+ if x != nil {
+ return x.ConfState
}
return nil
}
-func skipRecord(dAtA []byte) (n int, err error) {
- l := len(dAtA)
- iNdEx := 0
- depth := 0
- for iNdEx < l {
- var wire uint64
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- wire |= (uint64(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- wireType := int(wire & 0x7)
- switch wireType {
- case 0:
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- iNdEx++
- if dAtA[iNdEx-1] < 0x80 {
- break
- }
- }
- case 1:
- iNdEx += 8
- case 2:
- var length int
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- return 0, ErrIntOverflowRecord
- }
- if iNdEx >= l {
- return 0, io.ErrUnexpectedEOF
- }
- b := dAtA[iNdEx]
- iNdEx++
- length |= (int(b) & 0x7F) << shift
- if b < 0x80 {
- break
- }
- }
- if length < 0 {
- return 0, ErrInvalidLengthRecord
- }
- iNdEx += length
- case 3:
- depth++
- case 4:
- if depth == 0 {
- return 0, ErrUnexpectedEndOfGroupRecord
- }
- depth--
- case 5:
- iNdEx += 4
- default:
- return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
- }
- if iNdEx < 0 {
- return 0, ErrInvalidLengthRecord
- }
- if depth == 0 {
- return iNdEx, nil
- }
- }
- return 0, io.ErrUnexpectedEOF
-}
+
+var File_record_proto protoreflect.FileDescriptor
+
+const file_record_proto_rawDesc = "" +
+ "\n" +
+ "\frecord.proto\x12\x05walpb\x1a\x11raftpb/raft.proto\"B\n" +
+ "\x06Record\x12\x12\n" +
+ "\x04type\x18\x01 \x01(\x03R\x04type\x12\x10\n" +
+ "\x03crc\x18\x02 \x01(\rR\x03crc\x12\x12\n" +
+ "\x04data\x18\x03 \x01(\fR\x04data\"f\n" +
+ "\bSnapshot\x12\x14\n" +
+ "\x05index\x18\x01 \x01(\x04R\x05index\x12\x12\n" +
+ "\x04term\x18\x02 \x01(\x04R\x04term\x120\n" +
+ "\n" +
+ "conf_state\x18\x03 \x01(\v2\x11.raftpb.ConfStateR\tconfStateB-Z+go.etcd.io/etcd/server/v3/storage/wal/walpb"
var (
- ErrInvalidLengthRecord = fmt.Errorf("proto: negative length found during unmarshaling")
- ErrIntOverflowRecord = fmt.Errorf("proto: integer overflow")
- ErrUnexpectedEndOfGroupRecord = fmt.Errorf("proto: unexpected end of group")
+ file_record_proto_rawDescOnce sync.Once
+ file_record_proto_rawDescData []byte
)
+
+func file_record_proto_rawDescGZIP() []byte {
+ file_record_proto_rawDescOnce.Do(func() {
+ file_record_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_record_proto_rawDesc), len(file_record_proto_rawDesc)))
+ })
+ return file_record_proto_rawDescData
+}
+
+var file_record_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_record_proto_goTypes = []any{
+ (*Record)(nil), // 0: walpb.Record
+ (*Snapshot)(nil), // 1: walpb.Snapshot
+ (*raftpb.ConfState)(nil), // 2: raftpb.ConfState
+}
+var file_record_proto_depIdxs = []int32{
+ 2, // 0: walpb.Snapshot.conf_state:type_name -> raftpb.ConfState
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_record_proto_init() }
+func file_record_proto_init() {
+ if File_record_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_record_proto_rawDesc), len(file_record_proto_rawDesc)),
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_record_proto_goTypes,
+ DependencyIndexes: file_record_proto_depIdxs,
+ MessageInfos: file_record_proto_msgTypes,
+ }.Build()
+ File_record_proto = out.File
+ file_record_proto_goTypes = nil
+ file_record_proto_depIdxs = nil
+}
diff --git a/server/storage/wal/walpb/record.proto b/server/storage/wal/walpb/record.proto
index e1050fde4816..b1403df0c37c 100644
--- a/server/storage/wal/walpb/record.proto
+++ b/server/storage/wal/walpb/record.proto
@@ -1,26 +1,20 @@
syntax = "proto2";
package walpb;
-import "gogoproto/gogo.proto";
import "raftpb/raft.proto";
option go_package = "go.etcd.io/etcd/server/v3/storage/wal/walpb";
-option (gogoproto.marshaler_all) = true;
-option (gogoproto.sizer_all) = true;
-option (gogoproto.unmarshaler_all) = true;
-option (gogoproto.goproto_getters_all) = false;
-
message Record {
- optional int64 type = 1 [(gogoproto.nullable) = false];
- optional uint32 crc = 2 [(gogoproto.nullable) = false];
+ optional int64 type = 1;
+ optional uint32 crc = 2;
optional bytes data = 3;
}
// Keep in sync with raftpb.SnapshotMetadata.
message Snapshot {
- optional uint64 index = 1 [(gogoproto.nullable) = false];
- optional uint64 term = 2 [(gogoproto.nullable) = false];
+ optional uint64 index = 1;
+ optional uint64 term = 2;
// Field populated since >=etcd-3.5.0.
optional raftpb.ConfState conf_state = 3;
}
diff --git a/server/storage/wal/walpb/record_test.go b/server/storage/wal/walpb/record_test.go
index cdacb3d03f21..acf1032edc58 100644
--- a/server/storage/wal/walpb/record_test.go
+++ b/server/storage/wal/walpb/record_test.go
@@ -17,14 +17,14 @@ package walpb
import (
"testing"
- "github.com/golang/protobuf/descriptor"
+ "github.com/golang/protobuf/descriptor" //nolint:staticcheck // TODO: remove for a supported version
"go.etcd.io/raft/v3/raftpb"
)
func TestSnapshotMetadataCompatibility(t *testing.T) {
- _, snapshotMetadataMd := descriptor.ForMessage(&raftpb.SnapshotMetadata{})
- _, snapshotMd := descriptor.ForMessage(&Snapshot{})
+ _, snapshotMetadataMd := descriptor.ForMessage(&raftpb.SnapshotMetadata{}) //nolint:staticcheck // TODO: remove for a supported version
+ _, snapshotMd := descriptor.ForMessage(&Snapshot{}) //nolint:staticcheck // TODO: remove for a supported version
if len(snapshotMetadataMd.GetField()) != len(snapshotMd.GetField()) {
t.Errorf("Different number of fields in raftpb.SnapshotMetadata vs. walpb.Snapshot. " +
"They are supposed to be in sync.")
@@ -37,9 +37,10 @@ func TestValidateSnapshot(t *testing.T) {
snap *Snapshot
wantErr bool
}{
- {name: "empty", snap: &Snapshot{}, wantErr: false},
- {name: "invalid", snap: &Snapshot{Index: 5, Term: 3}, wantErr: true},
- {name: "valid", snap: &Snapshot{Index: 5, Term: 3, ConfState: &raftpb.ConfState{Voters: []uint64{0x00cad1}}}, wantErr: false},
+ {name: "empty", snap: &Snapshot{}, wantErr: true}, // index and term must be explicitly set
+ {name: "initial", snap: &Snapshot{Index: new(uint64(0)), Term: new(uint64(0))}, wantErr: false},
+ {name: "invalid", snap: &Snapshot{Index: new(uint64(5)), Term: new(uint64(3))}, wantErr: true},
+ {name: "valid", snap: &Snapshot{Index: new(uint64(5)), Term: new(uint64(3)), ConfState: &raftpb.ConfState{Voters: []uint64{0x00cad1}}}, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/server/verify/verify.go b/server/verify/verify.go
index 0dc99afc819f..1eb68b2b0b13 100644
--- a/server/verify/verify.go
+++ b/server/verify/verify.go
@@ -110,24 +110,24 @@ func MustVerifyIfEnabled(cfg Config) {
func validateConsistentIndex(cfg Config, hardstate *raftpb.HardState, snapshot *walpb.Snapshot, be backend.Backend) error {
index, term := schema.ReadConsistentIndex(be.ReadTx())
- if cfg.ExactIndex && index != hardstate.Commit {
- return fmt.Errorf("backend.ConsistentIndex (%v) expected == WAL.HardState.commit (%v)", index, hardstate.Commit)
+ if cfg.ExactIndex && index != hardstate.GetCommit() {
+ return fmt.Errorf("backend.ConsistentIndex (%v) expected == WAL.HardState.commit (%v)", index, hardstate.GetCommit())
}
- if cfg.ExactIndex && term != hardstate.Term {
- return fmt.Errorf("backend.Term (%v) expected == WAL.HardState.term, (%v)", term, hardstate.Term)
+ if cfg.ExactIndex && term != hardstate.GetTerm() {
+ return fmt.Errorf("backend.Term (%v) expected == WAL.HardState.term, (%v)", term, hardstate.GetTerm())
}
- if index > hardstate.Commit {
- return fmt.Errorf("backend.ConsistentIndex (%v) must be <= WAL.HardState.commit (%v)", index, hardstate.Commit)
+ if index > hardstate.GetCommit() {
+ return fmt.Errorf("backend.ConsistentIndex (%v) must be <= WAL.HardState.commit (%v)", index, hardstate.GetCommit())
}
- if term > hardstate.Term {
- return fmt.Errorf("backend.Term (%v) must be <= WAL.HardState.term, (%v)", term, hardstate.Term)
+ if term > hardstate.GetTerm() {
+ return fmt.Errorf("backend.Term (%v) must be <= WAL.HardState.term, (%v)", term, hardstate.GetTerm())
}
- if index < snapshot.Index {
- return fmt.Errorf("backend.ConsistentIndex (%v) must be >= last snapshot index (%v)", index, snapshot.Index)
+ if index < snapshot.GetIndex() {
+ return fmt.Errorf("backend.ConsistentIndex (%v) must be >= last snapshot index (%v)", index, snapshot.GetIndex())
}
- cfg.Logger.Info("verification: consistentIndex OK", zap.Uint64("backend-consistent-index", index), zap.Uint64("hardstate-commit", hardstate.Commit))
+ cfg.Logger.Info("verification: consistentIndex OK", zap.Uint64("backend-consistent-index", index), zap.Uint64("hardstate-commit", hardstate.GetCommit()))
return nil
}
@@ -144,5 +144,5 @@ func validateWAL(cfg Config) (*walpb.Snapshot, *raftpb.HardState, error) {
if err != nil {
return nil, nil, err
}
- return &snapshot, hardstate, nil
+ return snapshot, hardstate, nil
}
diff --git a/tests/LICENSE b/tests/LICENSE
index d64569567334..52fb9c29f2e2 100644
--- a/tests/LICENSE
+++ b/tests/LICENSE
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 The etcd Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/tests/antithesis/Makefile b/tests/antithesis/Makefile
new file mode 100644
index 000000000000..c2a00b56e0d1
--- /dev/null
+++ b/tests/antithesis/Makefile
@@ -0,0 +1,100 @@
+ANTITHESIS_ROOT :=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
+USER_ID := $(shell id -u)
+GROUP_ID := $(shell id -g)
+ARCH ?= $(shell go env GOARCH)
+REF = main
+IMAGE_TAG = latest
+
+CFG_NODE_COUNT ?= 3
+
+.PHONY: antithesis-build-client-docker-image
+antithesis-build-client-docker-image: validate-node-count
+ # This is a release image using go build without our tooling. Using the
+ # version defined in .go-version is the correct image tag.
+ docker build \
+ --build-arg GO_IMAGE_TAG=$(shell cat $(REPOSITORY_ROOT)/.go-version) \
+ --file $(ANTITHESIS_ROOT)/test-template/Dockerfile \
+ --tag etcd-client:latest \
+ $(REPOSITORY_ROOT)
+
+.PHONY: antithesis-build-etcd-image
+antithesis-build-etcd-image:
+ # Use the Workspace Go version from the go directive. This is the base image
+ # later in the Dockerfile, we'll set the correct toolchain.
+ docker build \
+ --build-arg GO_IMAGE_TAG=$(shell go work edit -json | jq .Go) \
+ --build-arg REF=$(REF) \
+ --tag etcd-server:latest \
+ $(ANTITHESIS_ROOT)/server/
+
+.PHONY: antithesis-build-etcd-image-release-3.4
+antithesis-build-etcd-image-release-3.4: REF=release-3.4
+antithesis-build-etcd-image-release-3.4: antithesis-build-etcd-image
+
+.PHONY: antithesis-build-etcd-image-release-3.5
+antithesis-build-etcd-image-release-3.5: REF=release-3.5
+antithesis-build-etcd-image-release-3.5: antithesis-build-etcd-image
+
+.PHONY: antithesis-build-etcd-image-release-3.6
+antithesis-build-etcd-image-release-3.6: REF=release-3.6
+antithesis-build-etcd-image-release-3.6: antithesis-build-etcd-image
+
+.PHONY: antithesis-build-etcd-image-main
+antithesis-build-etcd-image-main: REF=main
+antithesis-build-etcd-image-main: antithesis-build-etcd-image
+
+.PHONY: antithesis-build-config-image
+antithesis-build-config-image: validate-node-count
+ docker build -f config/Dockerfile config -t etcd-config:latest \
+ --build-arg IMAGE_TAG=$(IMAGE_TAG) \
+ --build-arg NODE_COUNT=$(CFG_NODE_COUNT)
+
+.PHONY: antithesis-docker-compose-up
+antithesis-docker-compose-up: validate-node-count
+ export USER_ID=$(USER_ID) && export GROUP_ID=$(GROUP_ID) && \
+ docker compose -f config/docker-compose-$(CFG_NODE_COUNT)-node.yml up
+
+.PHONY: antithesis-run-container-traffic
+antithesis-run-container-traffic: validate-node-count
+ export USER_ID=$(USER_ID) && export GROUP_ID=$(GROUP_ID) && \
+ docker compose -f config/docker-compose-$(CFG_NODE_COUNT)-node.yml exec client /opt/antithesis/test/v1/robustness/singleton_driver_traffic
+
+.PHONY: antithesis-run-container-validation
+antithesis-run-container-validation: validate-node-count
+ export USER_ID=$(USER_ID) && export GROUP_ID=$(GROUP_ID) && \
+ docker compose -f config/docker-compose-$(CFG_NODE_COUNT)-node.yml exec client /opt/antithesis/test/v1/robustness/finally_validation
+
+.PHONY: antithesis-run-local-traffic
+antithesis-run-local-traffic:
+ export ETCD_ROBUSTNESS_DATA_PATHS=/tmp/etcddata0,/tmp/etcddata1,/tmp/etcddata2 && export ETCD_ROBUSTNESS_REPORT_PATH=report && export ETCD_ROBUSTNESS_ENDPOINTS=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 && \
+ go run --race ./test-template/robustness/traffic/main.go
+
+.PHONY: antithesis-run-local-validation
+antithesis-run-local-validation:
+ export ETCD_ROBUSTNESS_DATA_PATHS=/tmp/etcddata0,/tmp/etcddata1,/tmp/etcddata2 && export ETCD_ROBUSTNESS_REPORT_PATH=report && export ETCD_ROBUSTNESS_ENDPOINTS=127.0.0.1:12379,127.0.0.1:22379,127.0.0.1:32379 && \
+ go run --race ./test-template/robustness/finally/main.go
+
+.PHONY: antithesis-clean
+antithesis-clean: validate-node-count
+ export USER_ID=$(USER_ID) && export GROUP_ID=$(GROUP_ID) && \
+ docker compose -f config/docker-compose-$(CFG_NODE_COUNT)-node.yml down --remove-orphans
+ rm -rf /tmp/etcddata0 /tmp/etcddata1 /tmp/etcddata2 /tmp/etcdreport
+
+.PHONY: validate-node-count
+validate-node-count:
+ @if [ "$(CFG_NODE_COUNT)" != "1" ] && [ "$(CFG_NODE_COUNT)" != "3" ]; then \
+ echo "CFG_NODE_COUNT must be either 1 or 3 (got $(CFG_NODE_COUNT))"; \
+ exit 1; \
+ fi
+
+REQUIRED_K8S_TOOLS := kubectl kind
+
+.PHONY: check-k8s-tools
+check-k8s-tools:
+ @for tool in $(REQUIRED_K8S_TOOLS); do \
+ if ! command -v $$tool >/dev/null 2>&1; then \
+ echo "Error: '$$tool' is missing. Please install it before continuing." >&2; \
+ exit 1; \
+ fi \
+ done
diff --git a/tests/antithesis/README.md b/tests/antithesis/README.md
new file mode 100644
index 000000000000..a7f93978def5
--- /dev/null
+++ b/tests/antithesis/README.md
@@ -0,0 +1,210 @@
+# etcd Antithesis tests
+
+This document describes the etcd test integration with [Antithesis].
+Antithesis provides a testing platform that allows you to explore edge cases, race conditions, and rare
+bugs that are difficult or impossible to reproduce in a normal environment.
+
+[Antithesis]: https://antithesis.com/
+
+## Robustness vs Antithesis tests
+
+[Antithesis] runs the robustness tests inside their
+[deterministic simulation testing](https://antithesis.com/resources/deterministic_simulation_testing/)
+environment and [fault injection](https://antithesis.com/docs/environment/fault_injection/).
+
+For more details on robustness tests, see the [robustness directory](../robustness).
+
+## Antithesis Setup
+
+The setup consists of a 3-node etcd cluster and a client container, orchestrated
+via [Docker Compose](https://antithesis.com/docs/getting_started/setup/).
+
+During the etcd Antithesis test suite the etcd server is built with the following patches:
+
+* **Critical code locations**: We replace etcd `gofail` comments (which signify
+ code locations important for failure injection in robustness tests) with
+ Antithesis `assert.Reachable`. This guides Antithesis to explore the
+ execution space around these points.
+* **Assertions**: We change etcd `verify` package assertions to Antithesis
+ `assert.Always`, encouraging the platform to try and break those assertions.
+* **Instrumentation**: The etcd binary is instrumented using
+ `antithesis-go-instrumentor` to enable coverage tracking and feedback for
+ the Antithesis platform.
+
+The Antithesis etcd tests configure the
+[Test Composer](https://antithesis.com/docs/test_templates/test_composer_reference/)
+in the following way:
+
+* **`entrypoint`**:
+ * Waits for all etcd nodes to be healthy.
+ * Emits the `setup_complete` message to Antithesis to start the testing phase.
+* **`singleton_driver_traffic`**:
+ * Generates robustness test traffic against the cluster while faults are injected.
+ * Runs as a [Singleton Driver Command], meaning it is the only one generating traffic.
+ * All generated traffic is saved as an operation history and stored on a shared volume.
+* **`finally_validation`**:
+ * Runs as a [Finally Command], meaning it is the last to run, with failure injection disabled.
+ * Reads the history of operations and validates them using the robustness test validation logic.
+ * Results of robustness tests are executed as Antithesis `assert.Always` assertions.
+ * Similar to robustness tests, it emits a visualization of the operations
+ history to an HTML file that is uploaded to the Antithesis platform.
+
+[Singleton Driver Command]: https://antithesis.com/docs/test_templates/test_composer_reference/#singleton-driver
+[Finally Command]: https://antithesis.com/docs/test_templates/test_composer_reference/#finally-command
+
+# Running tests with docker compose
+
+## Quickstart
+
+### 1. Build and Tag the Docker Image
+
+Run this command from the `antithesis/test-template` directory:
+
+```bash
+make antithesis-build-client-docker-image
+make antithesis-build-etcd-image
+```
+
+Both commands build etcd-server and etcd-client from the current branch. To build a different version of etcd you can use:
+
+```bash
+make antithesis-build-etcd-image REF=${GIT_REF}
+```
+
+### 2. (Optional) Check the Image Locally
+
+You can verify your new image is built:
+
+```bash
+docker images | grep etcd-client
+```
+
+It should show something like:
+
+```
+etcd-client latest
+```
+
+### 3. Use in Docker Compose
+
+Run the following command from the root directory for Antithesis tests (`tests/antithesis`):
+
+```bash
+make antithesis-docker-compose-up
+```
+
+The command uses the etcd client and server images built from step 1.
+
+The client will continuously check the health of the etcd nodes and print logs similar to:
+
+```
+[+] Running 4/4
+ ✔ Container etcd0 Created 0.0s
+ ✔ Container etcd2 Created 0.0s
+ ✔ Container etcd1 Created 0.0s
+ ✔ Container client Recreated 0.1s
+Attaching to client, etcd0, etcd1, etcd2
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.134294Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_ADVERTISE_CLIENT_URLS","variable-value":"http://etcd2.etcd:2379"}
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.138501Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_ADVERTISE_PEER_URLS","variable-value":"http://etcd2:2380"}
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.138646Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_CLUSTER","variable-value":"etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"}
+etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138434Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_ADVERTISE_CLIENT_URLS","variable-value":"http://etcd0.etcd:2379"}
+etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138582Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_ADVERTISE_PEER_URLS","variable-value":"http://etcd0:2380"}
+etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.138592Z","caller":"flags/flag.go:113","msg":"recognized and used environment variable","variable-name":"ETCD_INITIAL_CLUSTER","variable-value":"etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"}
+
+...
+...
+(skipping some repeated logs for brevity)
+...
+...
+
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.484698Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
+etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484092Z","caller":"embed/serve.go:210","msg":"serving client traffic insecurely; this is strongly discouraged!","traffic":"grpc+http","address":"[::]:2379"}
+etcd0 | {"level":"info","ts":"2025-04-14T07:23:25.484563Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.485101Z","caller":"v3rpc/health.go:61","msg":"grpc service status changed","service":"","status":"SERVING"}
+etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484130Z","caller":"etcdmain/main.go:44","msg":"notifying init daemon"}
+etcd2 | {"level":"info","ts":"2025-04-14T07:23:25.485782Z","caller":"embed/serve.go:210","msg":"serving client traffic insecurely; this is strongly discouraged!","traffic":"grpc+http","address":"[::]:2379"}
+etcd1 | {"level":"info","ts":"2025-04-14T07:23:25.484198Z","caller":"etcdmain/main.go:50","msg":"successfully notified init daemon"}
+client | Client [entrypoint]: starting...
+client | Client [entrypoint]: checking cluster health...
+client | Client [entrypoint]: connection successful with etcd0
+client | Client [entrypoint]: connection successful with etcd1
+client | Client [entrypoint]: connection successful with etcd2
+client | Client [entrypoint]: cluster is healthy!
+```
+
+And it will stay running indefinitely.
+
+### 4. Running the tests
+
+```bash
+make antithesis-run-container-traffic
+make antithesis-run-container-validation
+```
+
+Alternatively, with the etcd cluster from step 3, to run the tests locally without rebuilding the client image:
+
+```bash
+make antithesis-run-local-traffic
+make antithesis-run-local-validation
+```
+
+### 5. Prepare for next run
+
+Unfortunatelly robustness tests don't support running on non empty database.
+So for now you need to cleanup the storage before repeating the run or you will get "non empty database at start, required by model used for linearizability validation" error.
+
+```bash
+make antithesis-clean
+```
+
+## Troubleshooting
+
+- **Image Pull Errors**: If Docker can’t pull `etcd-client:latest`, make sure you built it locally (see the “Build and Tag” step) or push it to a registry that Compose can access.
+
+# Running Tests with Kubernetes (WIP)
+
+## Prerequisites
+
+Please make sure that you have the following tools installed on your local:
+
+- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
+- [kind](https://kind.sigs.k8s.io/docs/user/quick-start#installation)
+
+## Testing locally
+
+### Setting up the cluster and deploying the images
+
+#### 1. Ensure your access to a test kubernetes cluster
+
+You can use `kind` to create a local cluster to deploy the etcd-server and test client. Once you have `kind` installed, you can use the following command to create a local cluster:
+
+```bash
+kind create cluster
+```
+
+Alternatively, you can use any existing kubernetes cluster you have access to.
+
+#### 2. Build and load the images
+
+Please [build the client and server images](#1-build-and-tag-the-docker-image) first. Then load the images into the `kind` cluster:
+
+If you use `kind`, the cluster will need to have access to the images using the following commands:
+
+```bash
+kind load docker-image etcd-client:latest
+kind load docker-image etcd-server:latest
+```
+
+If you use something other than `kind`, please make sure the images are accessible to your cluster. This might involve pushing the images to a container registry that your cluster can pull from.
+
+#### 3. Deploy the kubernetes manifests
+
+```bash
+kubectl apply -f ./config/manifests
+```
+
+### Tearing down the cluster
+
+```bash
+kind delete cluster --name kind
+```
diff --git a/tests/antithesis/config/Dockerfile b/tests/antithesis/config/Dockerfile
new file mode 100644
index 000000000000..7cd60d752fb6
--- /dev/null
+++ b/tests/antithesis/config/Dockerfile
@@ -0,0 +1,12 @@
+ARG GO_VERSION=1.25.5
+
+FROM golang:$GO_VERSION AS build
+RUN go install github.com/a8m/envsubst/cmd/envsubst@v1.4.3
+
+ARG IMAGE_TAG
+ARG NODE_COUNT
+COPY docker-compose-${NODE_COUNT}-node.yml /docker-compose.yml.template
+RUN IMAGE_TAG=${IMAGE_TAG} cat /docker-compose.yml.template | envsubst > /docker-compose.yml
+
+FROM scratch
+COPY --from=build /docker-compose.yml /docker-compose.yml
diff --git a/tests/antithesis/config/docker-compose-1-node.yml b/tests/antithesis/config/docker-compose-1-node.yml
new file mode 100644
index 000000000000..265f5fd9e84b
--- /dev/null
+++ b/tests/antithesis/config/docker-compose-1-node.yml
@@ -0,0 +1,62 @@
+---
+services:
+ # This is needed for creating non-root data folders on host.
+ # By default, if the folders don't exist when mounting, compose creates them with root as owner.
+ # With root owner, accessing the WAL files from local tests will fail due to an unauthorized access error.
+ init:
+ image: 'docker.io/library/ubuntu:latest'
+ user: root
+ group_add:
+ - '${GROUP_ID:-root}'
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcddata0
+ - ${ETCD_ROBUSTNESS_REPORT_PATH:-/tmp/etcdreport}:/var/report
+ command:
+ - /bin/sh
+ - -c
+ - |
+ rm -rf /var/etcddata0/* /var/report/*
+ chown -R ${USER_ID:-root}:${GROUP_ID:-root} /var/etcddata0 /var/report
+
+ etcd0:
+ image: 'etcd-server:${IMAGE_TAG:-latest}'
+ container_name: etcd0
+ hostname: etcd0
+ environment:
+ ETCD_NAME: "etcd0"
+ ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd0:2380"
+ ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
+ ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
+ ETCD_ADVERTISE_CLIENT_URLS: "http://etcd0.etcd:2379"
+ ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
+ ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380"
+ ETCD_INITIAL_CLUSTER_STATE: "new"
+ ETCD_DATA_DIR: "/var/etcd/data"
+ ETCD_SNAPSHOT_CATCHUP_ENTRIES: 100
+ ETCD_SNAPSHOT_COUNT: 50
+ ETCD_COMPACTION_BATCH_LIMIT: 10
+ ETCD_VERIFY: "all"
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ depends_on:
+ init:
+ condition: service_completed_successfully
+ ports:
+ - 12379:2379
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcd/data
+
+ client:
+ image: 'etcd-client:${IMAGE_TAG:-latest}'
+ container_name: client
+ entrypoint: ["/opt/antithesis/entrypoint/entrypoint"]
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ environment:
+ ETCD_ROBUSTNESS_ENDPOINTS: "etcd0:2379"
+ ETCD_ROBUSTNESS_DATA_PATHS: "/var/etcddata0"
+ ETCD_ROBUSTNESS_REPORT_PATH: "/var/report"
+ depends_on:
+ etcd0:
+ condition: service_started
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcddata0
+ - ${ETCD_ROBUSTNESS_REPORT_PATH:-/tmp/etcdreport}:/var/report
diff --git a/tests/antithesis/config/docker-compose-3-node.yml b/tests/antithesis/config/docker-compose-3-node.yml
new file mode 100644
index 000000000000..bc779af0de48
--- /dev/null
+++ b/tests/antithesis/config/docker-compose-3-node.yml
@@ -0,0 +1,124 @@
+---
+services:
+ # This is needed for creating non-root data folders on host.
+ # By default, if the folders don't exist when mounting, compose creates them with root as owner.
+ # With root owner, accessing the WAL files from local tests will fail due to an unauthorized access error.
+ init:
+ image: 'docker.io/library/ubuntu:latest'
+ user: root
+ group_add:
+ - '${GROUP_ID:-root}'
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcddata0
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}1:/var/etcddata1
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}2:/var/etcddata2
+ - ${ETCD_ROBUSTNESS_REPORT_PATH:-/tmp/etcdreport}:/var/report
+ command:
+ - /bin/sh
+ - -c
+ - |
+ rm -rf /var/etcddata0/* /var/etcddata1/* /var/etcddata2/* /var/report/*
+ chown -R ${USER_ID:-root}:${GROUP_ID:-root} /var/etcddata0 /var/etcddata1 /var/etcddata2 /var/report
+
+ etcd0:
+ image: 'etcd-server:${IMAGE_TAG:-latest}'
+ container_name: etcd0
+ hostname: etcd0
+ environment:
+ ETCD_NAME: "etcd0"
+ ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd0:2380"
+ ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
+ ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
+ ETCD_ADVERTISE_CLIENT_URLS: "http://etcd0.etcd:2379"
+ ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
+ ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
+ ETCD_INITIAL_CLUSTER_STATE: "new"
+ ETCD_DATA_DIR: "/var/etcd/data"
+ ETCD_SNAPSHOT_CATCHUP_ENTRIES: 100
+ ETCD_SNAPSHOT_COUNT: 50
+ ETCD_COMPACTION_BATCH_LIMIT: 10
+ ETCD_VERIFY: "all"
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ depends_on:
+ init:
+ condition: service_completed_successfully
+ ports:
+ - 12379:2379
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcd/data
+
+ etcd1:
+ image: 'etcd-server:${IMAGE_TAG:-latest}'
+ container_name: etcd1
+ hostname: etcd1
+ environment:
+ ETCD_NAME: "etcd1"
+ ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd1:2380"
+ ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
+ ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
+ ETCD_ADVERTISE_CLIENT_URLS: "http://etcd1.etcd:2379"
+ ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
+ ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
+ ETCD_INITIAL_CLUSTER_STATE: "new"
+ ETCD_DATA_DIR: "/var/etcd/data"
+ ETCD_SNAPSHOT_CATCHUP_ENTRIES: 100
+ ETCD_SNAPSHOT_COUNT: 50
+ ETCD_COMPACTION_BATCH_LIMIT: 10
+ ETCD_VERIFY: "all"
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ depends_on:
+ init:
+ condition: service_completed_successfully
+ ports:
+ - 22379:2379
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}1:/var/etcd/data
+
+ etcd2:
+ image: 'etcd-server:${IMAGE_TAG:-latest}'
+ container_name: etcd2
+ hostname: etcd2
+ environment:
+ ETCD_NAME: "etcd2"
+ ETCD_INITIAL_ADVERTISE_PEER_URLS: "http://etcd2:2380"
+ ETCD_LISTEN_PEER_URLS: "http://0.0.0.0:2380"
+ ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
+ ETCD_ADVERTISE_CLIENT_URLS: "http://etcd2.etcd:2379"
+ ETCD_INITIAL_CLUSTER_TOKEN: "etcd-cluster-1"
+ ETCD_INITIAL_CLUSTER: "etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380"
+ ETCD_INITIAL_CLUSTER_STATE: "new"
+ ETCD_DATA_DIR: "/var/etcd/data"
+ ETCD_SNAPSHOT_CATCHUP_ENTRIES: 100
+ ETCD_SNAPSHOT_COUNT: 50
+ ETCD_COMPACTION_BATCH_LIMIT: 10
+ ETCD_VERIFY: "all"
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ depends_on:
+ init:
+ condition: service_completed_successfully
+ ports:
+ - 32379:2379
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}2:/var/etcd/data
+
+ client:
+ image: 'etcd-client:${IMAGE_TAG:-latest}'
+ container_name: client
+ entrypoint: ["/opt/antithesis/entrypoint/entrypoint"]
+ user: "${USER_ID:-root}:${GROUP_ID:-root}"
+ environment:
+ ETCD_ROBUSTNESS_ENDPOINTS: "etcd0:2379,etcd1:2379,etcd2:2379"
+ ETCD_ROBUSTNESS_DATA_PATHS: "/var/etcddata0,/var/etcddata1,/var/etcddata2"
+ ETCD_ROBUSTNESS_REPORT_PATH: "/var/report"
+ depends_on:
+ etcd0:
+ condition: service_started
+ etcd1:
+ condition: service_started
+ etcd2:
+ condition: service_started
+ volumes:
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}0:/var/etcddata0
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}1:/var/etcddata1
+ - ${ETCD_ROBUSTNESS_DATA_PATH_PREFIX:-/tmp/etcddata}2:/var/etcddata2
+ - ${ETCD_ROBUSTNESS_REPORT_PATH:-/tmp/etcdreport}:/var/report
diff --git a/tests/antithesis/config/manifests/default-etcd-3-replicas.yaml b/tests/antithesis/config/manifests/default-etcd-3-replicas.yaml
new file mode 100644
index 000000000000..ceaeb8ede006
--- /dev/null
+++ b/tests/antithesis/config/manifests/default-etcd-3-replicas.yaml
@@ -0,0 +1,103 @@
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: etcd
+ namespace: default
+spec:
+ type: ClusterIP
+ clusterIP: None
+ publishNotReadyAddresses: true
+ ports:
+ - name: client
+ port: 2379
+ targetPort: client
+ - name: peer
+ port: 2380
+ targetPort: peer
+ selector:
+ app: etcd
+---
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: etcd
+ namespace: default
+spec:
+ selector:
+ matchLabels:
+ app: etcd
+ serviceName: etcd
+ replicas: 3
+ podManagementPolicy: Parallel
+ template:
+ metadata:
+ labels:
+ app: etcd
+ spec:
+ terminationGracePeriodSeconds: 10
+ containers:
+ - name: etcd-server
+ image: etcd-server:latest
+ imagePullPolicy: Never
+ env:
+ - name: ETCD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ - name: CLIENT_PORT
+ value: "2379"
+ - name: PEER_PORT
+ value: "2380"
+ - name: ETCD_INITIAL_ADVERTISE_PEER_URLS
+ value: http://$(ETCD_NAME).etcd.default.svc.cluster.local:$(PEER_PORT)
+ - name: ETCD_LISTEN_PEER_URLS
+ value: "http://0.0.0.0:$(PEER_PORT)"
+ - name: ETCD_LISTEN_CLIENT_URLS
+ value: "http://0.0.0.0:$(CLIENT_PORT)"
+ - name: ETCD_ADVERTISE_CLIENT_URLS
+ value: "https://$(ETCD_NAME).etcd:$(CLIENT_PORT)"
+ - name: ETCD_INITIAL_CLUSTER_TOKEN
+ value: "etcd-cluster-1"
+ - name: ETCD_INITIAL_CLUSTER
+ value: "etcd-0=http://etcd-0.etcd.default.svc.cluster.local:$(PEER_PORT),etcd-1=http://etcd-1.etcd.default.svc.cluster.local:$(PEER_PORT),etcd-2=http://etcd-2.etcd.default.svc.cluster.local:$(PEER_PORT)"
+ - name: ETCD_INITIAL_CLUSTER_STATE
+ value: "new"
+ - name: ETCD_DATA_DIR
+ value: "/var/etcd/data"
+ - name: ETCD_SNAPSHOT_CATCHUP_ENTRIES
+ value: "100"
+ - name: ETCD_SNAPSHOT_COUNT
+ value: "50"
+ - name: ETCD_COMPACTION_BATCH_LIMIT
+ value: "10"
+ - name: ETCD_VERIFY
+ value: "all"
+ ports:
+ - containerPort: 2379
+ name: client
+ protocol: TCP
+ - containerPort: 2380
+ name: peer
+ protocol: TCP
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: client
+ livenessProbe:
+ httpGet:
+ path: /livez
+ port: client
+ volumeMounts:
+ - name: data
+ mountPath: /var/etcd/data
+ volumeClaimTemplates:
+ - metadata:
+ name: data
+ spec:
+ storageClassName: standard
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 200Mi
diff --git a/tests/antithesis/server/Dockerfile b/tests/antithesis/server/Dockerfile
new file mode 100644
index 000000000000..7814e4caef6a
--- /dev/null
+++ b/tests/antithesis/server/Dockerfile
@@ -0,0 +1,75 @@
+ARG ARCH=amd64
+ARG GO_IMAGE_TAG
+
+FROM golang:$GO_IMAGE_TAG AS build
+
+# cloning etcd
+ARG REF=main
+RUN git clone --depth=1 https://github.com/etcd-io/etcd.git --branch=${REF} /etcd
+RUN go env -w GOTOOLCHAIN="go$(cat .go-version)"
+
+# inject assertions in place of gofail
+WORKDIR /etcd/server
+RUN go install golang.org/x/tools/cmd/goimports@latest
+RUN go get github.com/antithesishq/antithesis-sdk-go@v0.4.4
+RUN for file in $(grep -rl '// gofail'); do sed -i 's|\/\/ gofail.*var \([[:alnum:]]*\) .*|assert\.Reachable("\1", nil)|' $file; goimports -w $file; done
+RUN go mod tidy
+
+# replace verify with antithesis
+WORKDIR /etcd/client/pkg/verify
+COPY inject/verify.patch verify.patch
+RUN if [ "${REF}" = "main" ]; then git apply verify.patch; fi
+WORKDIR /etcd/client/pkg
+RUN go mod tidy
+WORKDIR /etcd
+
+# setup go mod
+RUN go mod download
+
+# install instrumentor
+RUN go get github.com/antithesishq/antithesis-sdk-go@v0.4.4
+RUN go install github.com/antithesishq/antithesis-sdk-go/tools/antithesis-go-instrumentor@a802e8810442e01d16b3e9df77d7ce3875e36e55 # v0.4.3
+RUN go mod tidy
+
+# compile etcd server with instrumentor
+RUN mkdir /etcd_instrumented
+RUN `go env GOPATH`/bin/antithesis-go-instrumentor /etcd /etcd_instrumented
+
+# Remove this once antithesis fixed the bug
+# The instrumentor's notifier library doesn't preserve the go and toolchain directives
+# This makes any subsequent `go mod tidy` run remove all the directives and replaced them with one generated by the notifier folder
+# Updating /etcd_instrumented/notifier/go.mod with the original directives would prevent all subsequent `go mod tidy` from removing the directives
+# However `go mod tidy` is already invoked by the instrumentor, so it needs to be fixed as well.
+RUN for d in /etcd_instrumented/customer /etcd_instrumented/notifier; do \
+ cd ${d}; \
+ go mod edit -go=$(grep '^go ' /etcd/go.mod | cut -f2 -d' '); \
+ go mod edit -toolchain=$(grep 'toolchain ' /etcd/go.mod | cut -f2 -d' '); \
+ done
+
+WORKDIR /etcd_instrumented/customer
+
+# Some previous versions hardcode CGO_ENABLED=0
+RUN find . -type f -exec sed -i 's/CGO_ENABLED=0/CGO_ENABLED=${CGO_ENABLED}/' {} +
+# Some previous versions explicitly need gobin, which could no longer be installed with go get
+RUN go install github.com/myitcv/gobin@v0.0.14
+# 3.4.0 has vendoring. need to do this after instrumentation or else build fails
+RUN if [ -d "vendor" ]; then go mod vendor; fi
+
+# The instrumentation adds code and packages. Need go mod tidy for all modules before building
+RUN for d in server etcdutl etcdctl; do \
+ (cd ${d} && go mod tidy || true); \
+ done
+
+# The instrumentation also adds a new main file which clashes with dummy.go found in non release-3.4 branches
+RUN if [ -f "dummy.go" ]; then sed -i 's/package main_test/package main/' dummy.go; fi
+RUN CGO_ENABLED=1 GO_GCFLAGS="all=-N -l" make build
+RUN go install github.com/go-delve/delve/cmd/dlv@latest
+
+FROM ubuntu:24.04
+COPY --from=build /go/bin/dlv /bin/dlv
+COPY --from=build /etcd_instrumented/ /etcd
+# Move symbols to /symbols directory https://antithesis.com/docs/instrumentation/#symbolization
+RUN mv /etcd/symbols /symbols
+
+EXPOSE 2379 2380
+CMD ["/etcd/customer/bin/etcd"]
diff --git a/tests/antithesis/server/inject/verify.patch b/tests/antithesis/server/inject/verify.patch
new file mode 100644
index 000000000000..b0313c73409b
--- /dev/null
+++ b/tests/antithesis/server/inject/verify.patch
@@ -0,0 +1,22 @@
+diff --git a/client/pkg/verify/verify.go b/client/pkg/verify/verify.go
+index cb48d8ff0..095f890ec 100644
+--- a/client/pkg/verify/verify.go
++++ b/client/pkg/verify/verify.go
+@@ -18,6 +18,8 @@ import (
+ "fmt"
+ "os"
+ "strings"
++
++ "github.com/antithesishq/antithesis-sdk-go/assert"
+ )
+
+ const envVerify = "ETCD_VERIFY"
+@@ -69,7 +71,7 @@ func DisableVerifications() func() {
+ func Verify(msg string, f VerifyFunc) {
+ if IsVerificationEnabled(envVerifyValueAssert) {
+ ok, details := f()
+- verifier(ok, msg, details)
++ assert.Always(ok, msg, details)
+ }
+ }
+
diff --git a/tests/antithesis/test-template/Dockerfile b/tests/antithesis/test-template/Dockerfile
new file mode 100644
index 000000000000..9f40a31db433
--- /dev/null
+++ b/tests/antithesis/test-template/Dockerfile
@@ -0,0 +1,14 @@
+ARG GO_IMAGE_TAG
+ARG ARCH=amd64
+
+FROM golang:$GO_IMAGE_TAG AS build
+WORKDIR /build
+COPY . .
+
+WORKDIR /build/tests
+RUN go build -o /opt/antithesis/entrypoint/entrypoint -race ./antithesis/test-template/entrypoint/main.go
+RUN go build -o /opt/antithesis/test/v1/robustness/singleton_driver_traffic -race ./antithesis/test-template/robustness/traffic/main.go
+RUN go build -o /opt/antithesis/test/v1/robustness/finally_validation -race ./antithesis/test-template/robustness/finally/main.go
+
+FROM ubuntu:24.04
+COPY --from=build /opt/ /opt/
diff --git a/tests/antithesis/test-template/entrypoint/main.go b/tests/antithesis/test-template/entrypoint/main.go
new file mode 100644
index 000000000000..9d68c3292118
--- /dev/null
+++ b/tests/antithesis/test-template/entrypoint/main.go
@@ -0,0 +1,92 @@
+// Copyright 2025 The etcd Authors
+//
+// 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.
+
+//go:build cgo && amd64
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/antithesishq/antithesis-sdk-go/lifecycle"
+
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/antithesis/test-template/robustness/common"
+)
+
+// Sleep duration
+const SLEEP = 10
+
+// CheckHealth checks health of all etcd nodes
+func CheckHealth() bool {
+ hosts, _, _ := common.GetPaths()
+
+ // iterate over each host and check health
+ for _, host := range hosts {
+ cli, err := clientv3.New(clientv3.Config{
+ Endpoints: []string{fmt.Sprintf("http://%s", host)},
+ DialTimeout: 5 * time.Second,
+ })
+ if err != nil {
+ fmt.Printf("Client [entrypoint]: connection failed with %s\n", host)
+ fmt.Printf("Client [entrypoint]: error: %v\n", err)
+ return false
+ }
+
+ defer func() {
+ cErr := cli.Close()
+ if cErr != nil {
+ fmt.Printf("Client [entrypoint]: error closing connection: %v\n", cErr)
+ }
+ }()
+
+ // fetch the key setting-up to confirm that the node is available
+ _, err = cli.Get(context.Background(), "setting-up")
+ if err != nil {
+ fmt.Printf("Client [entrypoint]: connection failed with %s\n", host)
+ fmt.Printf("Client [entrypoint]: error: %v\n", err)
+ return false
+ }
+
+ fmt.Printf("Client [entrypoint]: connection successful with %s\n", host)
+ }
+
+ return true
+}
+
+func main() {
+ fmt.Println("Client [entrypoint]: starting...")
+
+ // run loop until all nodes are healthy
+ for {
+ fmt.Println("Client [entrypoint]: checking cluster health...")
+ if CheckHealth() {
+ fmt.Println("Client [entrypoint]: cluster is healthy!")
+ break
+ }
+ fmt.Printf("Client [entrypoint]: cluster is not healthy. retrying in %d seconds...\n", SLEEP)
+ time.Sleep(SLEEP * time.Second)
+ }
+
+ // signal that the setup looks complete
+ lifecycle.SetupComplete(
+ map[string]string{
+ "Message": "ETCD cluster is healthy",
+ },
+ )
+
+ select {}
+}
diff --git a/tests/antithesis/test-template/robustness/common/path.go b/tests/antithesis/test-template/robustness/common/path.go
new file mode 100644
index 000000000000..ccf81b44097d
--- /dev/null
+++ b/tests/antithesis/test-template/robustness/common/path.go
@@ -0,0 +1,69 @@
+// Copyright 2025 The etcd Authors
+//
+// 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.
+
+//go:build cgo && amd64
+
+package common
+
+import (
+ "fmt"
+ "os"
+ "strings"
+)
+
+const (
+ endpointsEnv = "ETCD_ROBUSTNESS_ENDPOINTS"
+ dataPathsEnv = "ETCD_ROBUSTNESS_DATA_PATHS"
+ reportPathEnv = "ETCD_ROBUSTNESS_REPORT_PATH"
+)
+
+func GetPaths() (hosts []string, reportPath string, dataPaths map[string]string) {
+ // Check for environment variable overrides first
+ envDataPathsStr := os.Getenv(dataPathsEnv)
+ envReportPath := os.Getenv(reportPathEnv)
+ envEndpointsStr := os.Getenv(endpointsEnv)
+
+ // Temporary disable to make PR simpler to review
+ //revive:disable:early-return
+ if envEndpointsStr != "" {
+ hosts = strings.Split(envEndpointsStr, ",")
+ for i, host := range hosts {
+ hosts[i] = strings.TrimSpace(host)
+ }
+ } else {
+ panic(fmt.Sprintf("No endpoints specified in %s", endpointsEnv))
+ }
+
+ if envReportPath != "" {
+ reportPath = envReportPath
+ } else {
+ panic(fmt.Sprintf("No report path specified in %s", reportPathEnv))
+ }
+
+ if envDataPathsStr != "" {
+ envDataPaths := strings.Split(envDataPathsStr, ",")
+ if len(envDataPaths) != len(hosts) {
+ panic(fmt.Sprintf("Mismatched number of endpoints and data paths: %d endpoints, %d data paths", len(hosts), len(envDataPaths)))
+ }
+
+ dataPaths = make(map[string]string)
+ for i, endpoint := range hosts {
+ dataPaths[endpoint] = strings.TrimSpace(envDataPaths[i])
+ }
+ } else {
+ panic(fmt.Sprintf("No data paths specified in %s", dataPathsEnv))
+ }
+ //revive:enable:early-return
+ return hosts, reportPath, dataPaths
+}
diff --git a/tests/antithesis/test-template/robustness/finally/main.go b/tests/antithesis/test-template/robustness/finally/main.go
new file mode 100644
index 000000000000..e20b88a6f0d1
--- /dev/null
+++ b/tests/antithesis/test-template/robustness/finally/main.go
@@ -0,0 +1,79 @@
+// Copyright 2025 The etcd Authors
+//
+// 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.
+
+//go:build cgo && amd64
+
+package main
+
+import (
+ "maps"
+ "os"
+ "path/filepath"
+ "slices"
+ "time"
+
+ "github.com/antithesishq/antithesis-sdk-go/assert"
+ "go.uber.org/zap"
+
+ "go.etcd.io/etcd/tests/v3/antithesis/test-template/robustness/common"
+ "go.etcd.io/etcd/tests/v3/robustness/report"
+ "go.etcd.io/etcd/tests/v3/robustness/validate"
+)
+
+const (
+ reportFileName = "history.html"
+)
+
+func main() {
+ _, reportPath, dirs := common.GetPaths()
+
+ lg, err := zap.NewProduction()
+ if err != nil {
+ panic(err)
+ }
+ reports, err := report.LoadClientReports(reportPath)
+ assert.Always(err == nil, "Loaded client reports", map[string]any{"error": err})
+ tf, err := report.LoadTrafficDetail(reportPath)
+ if err != nil && !os.IsNotExist(err) {
+ panic(err)
+ }
+
+ result := validateReports(lg, dirs, reports, tf)
+ if err := result.Linearization.Visualize(lg, filepath.Join(reportPath, reportFileName)); err != nil {
+ panic(err)
+ }
+}
+
+func validateReports(lg *zap.Logger, serversDataPath map[string]string, reports []report.ClientReport, tf report.TrafficDetail) validate.RobustnessResult {
+ persistedRequests, err := report.PersistedRequests(lg, slices.Collect(maps.Values(serversDataPath)))
+ assertResult(validate.ResultFromError(err), "Loaded persisted requests")
+
+ validateConfig := validate.Config{ExpectRevisionUnique: tf.ExpectUniqueRevision}
+ result := validate.ValidateAndReturnVisualize(lg, validateConfig, reports, persistedRequests, 5*time.Minute)
+ assertResult(result.Assumptions, "Validation assumptions fulfilled")
+ assertResult(result.Linearization.Result, "Linearization validation passes")
+ assertResult(result.Watch, "Watch validation passes")
+ assertResult(result.Serializable, "Serializable validation passes")
+ lg.Info("Completed robustness validation")
+ return result
+}
+
+func assertResult(result validate.Result, name string) {
+ switch result.Status {
+ case validate.Success, validate.Failure:
+ assert.Always(result.Status == validate.Success, name, map[string]any{"msg": result.Message})
+ default:
+ assert.Unreachable(name, map[string]any{"error": result.Error().Error()})
+ }
+}
diff --git a/tests/antithesis/test-template/robustness/traffic/main.go b/tests/antithesis/test-template/robustness/traffic/main.go
new file mode 100644
index 000000000000..50559083eaa4
--- /dev/null
+++ b/tests/antithesis/test-template/robustness/traffic/main.go
@@ -0,0 +1,243 @@
+// Copyright 2025 The etcd Authors
+//
+// 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.
+
+//go:build cgo && amd64
+
+package main
+
+import (
+ "context"
+ "math/rand/v2"
+ "os"
+ "slices"
+ "sync"
+ "time"
+
+ "github.com/antithesishq/antithesis-sdk-go/assert"
+ "go.uber.org/zap"
+ "golang.org/x/sync/errgroup"
+ "golang.org/x/time/rate"
+
+ "go.etcd.io/etcd/tests/v3/antithesis/test-template/robustness/common"
+ "go.etcd.io/etcd/tests/v3/robustness/client"
+ "go.etcd.io/etcd/tests/v3/robustness/identity"
+ robustnessrand "go.etcd.io/etcd/tests/v3/robustness/random"
+ "go.etcd.io/etcd/tests/v3/robustness/report"
+ "go.etcd.io/etcd/tests/v3/robustness/traffic"
+)
+
+var (
+ DefaultWatchInterval = 250 * time.Millisecond
+
+ profile = traffic.Profile{
+ KeyValue: &traffic.KeyValue{
+ MinimalQPS: 100,
+ MaximalQPS: 1000,
+ BurstableQPS: 1000,
+ MemberClientCount: 3,
+ ClusterClientCount: 1,
+ MaxNonUniqueRequestConcurrency: 3,
+ },
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ }
+ trafficNames = []string{
+ "etcd",
+ "kubernetes",
+ }
+ traffics = []traffic.Traffic{
+ traffic.EtcdPutDeleteLease,
+ traffic.Kubernetes,
+ }
+)
+
+func main() {
+ hosts, reportPath, etcdetcdDataPaths := common.GetPaths()
+
+ ctx := context.Background()
+ baseTime := time.Now()
+ duration := time.Duration(robustnessrand.RandRange(5, 15) * int64(time.Second))
+
+ lg, err := zap.NewProduction()
+ if err != nil {
+ panic(err)
+ }
+ choice := rand.IntN(len(traffics))
+ tf := traffics[choice]
+ lg.Info("Traffic", zap.String("Type", trafficNames[choice]))
+ r, err := report.NewTestReport(lg, reportPath, etcdetcdDataPaths, &report.TrafficDetail{ExpectUniqueRevision: tf.ExpectUniqueRevision()})
+ if err != nil {
+ lg.Fatal("Failed to create test report", zap.Error(err))
+ }
+ defer func() {
+ if err = r.SaveEtcdData(); err != nil {
+ lg.Error("Failed to save traffic generation report", zap.Error(err))
+ }
+ }()
+
+ lg.Info("Start traffic generation", zap.Duration("duration", duration), zap.String("base-time", baseTime.UTC().Format("2006-01-02T15:04:05.000000Z0700")))
+ clientReports, err := runTraffic(ctx, lg, tf, hosts, baseTime, duration)
+ r.SetClientReports(clientReports)
+ if err != nil {
+ lg.Error("Failed to generate traffic")
+ panic(err)
+ }
+}
+
+func runTraffic(ctx context.Context, lg *zap.Logger, tf traffic.Traffic, hosts []string, baseTime time.Time, duration time.Duration) ([]report.ClientReport, error) {
+ ids := identity.NewIDProvider()
+ trafficSet := client.NewSet(ids, baseTime)
+ defer trafficSet.Close()
+ err := traffic.CheckEmptyDatabaseAtStart(ctx, lg, hosts, trafficSet)
+ if err != nil {
+ lg.Fatal("Failed empty database at start check", zap.Error(err))
+ }
+ maxRevisionChan := make(chan int64, 1)
+ g := errgroup.Group{}
+ startTime := time.Since(baseTime)
+ g.Go(func() error {
+ defer close(maxRevisionChan)
+ simulateTraffic(ctx, lg, tf, hosts, trafficSet, duration)
+ maxRevision := report.OperationsMaxRevision(trafficSet.Reports())
+ maxRevisionChan <- maxRevision
+ lg.Info("Finished simulating Traffic", zap.Int64("max-revision", maxRevision))
+ return nil
+ })
+ watchSet := client.NewSet(ids, baseTime)
+ defer watchSet.Close()
+ g.Go(func() error {
+ err := client.CollectClusterWatchEvents(ctx, client.CollectClusterWatchEventsParam{
+ Lg: lg,
+ Endpoints: hosts,
+ MaxRevisionChan: maxRevisionChan,
+ ClientSet: watchSet,
+ })
+ return err
+ })
+ if err := g.Wait(); err != nil {
+ return nil, err
+ }
+ endTime := time.Since(baseTime)
+ reports := slices.Concat(trafficSet.Reports(), watchSet.Reports())
+ totalStats := traffic.CalculateStats(reports, startTime, endTime)
+ lg.Info("Completed traffic generation",
+ zap.Int("successes", totalStats.Successes),
+ zap.Int("failures", totalStats.Failures),
+ zap.Float64("successRate", totalStats.SuccessRate()),
+ zap.Duration("period", totalStats.Period),
+ zap.Float64("qps", totalStats.QPS()),
+ )
+ return reports, nil
+}
+
+func simulateTraffic(ctx context.Context, lg *zap.Logger, tf traffic.Traffic, hosts []string, clientSet *client.ClientSet, duration time.Duration) {
+ var wg sync.WaitGroup
+ leaseStorage := identity.NewLeaseIDStorage()
+ kubernetesStorage := traffic.NewKubernetesStorage()
+ limiter := rate.NewLimiter(rate.Limit(profile.KeyValue.MaximalQPS), profile.KeyValue.BurstableQPS)
+ concurrencyLimiter := traffic.NewConcurrencyLimiter(profile.KeyValue.MaxNonUniqueRequestConcurrency)
+ finish := closeAfter(ctx, duration)
+ keyStore := traffic.NewKeyStore(10, "key")
+ err := traffic.SimulateKeyValueTraffic(ctx, &wg, profile.KeyValue, hosts, clientSet, tf, traffic.RunTrafficLoopParam{
+ QPSLimiter: limiter,
+ IDs: clientSet.IdentityProvider(),
+ LeaseIDStorage: leaseStorage,
+ NonUniqueRequestConcurrencyLimiter: concurrencyLimiter,
+ KeyStore: keyStore,
+ Storage: kubernetesStorage,
+ Finish: finish,
+ })
+ if err != nil {
+ assert.Unreachable("Client failed to connect to an etcd host", map[string]any{"endpoints": hosts, "error": err})
+ os.Exit(1)
+ }
+ if profile.Watch != nil {
+ err := traffic.SimulateWatchTraffic(ctx, &wg, profile.Watch, hosts, clientSet, tf, traffic.RunWatchLoopParam{
+ Config: *profile.Watch,
+ QPSLimiter: limiter,
+ KeyStore: keyStore,
+ Storage: kubernetesStorage,
+ Finish: finish,
+ Logger: lg,
+ })
+ if err != nil {
+ assert.Unreachable("Client failed to connect to an etcd host", map[string]any{"endpoints": hosts, "error": err})
+ os.Exit(1)
+ }
+ }
+ if profile.Compaction != nil {
+ err := traffic.SimulateCompactionTraffic(ctx, &wg, profile.Compaction, hosts, clientSet, tf, traffic.RunCompactLoopParam{
+ Period: profile.Compaction.Period,
+ Finish: finish,
+ })
+ if err != nil {
+ assert.Unreachable("Client failed to connect to an etcd host", map[string]any{"endpoints": hosts, "error": err})
+ os.Exit(1)
+ }
+ }
+ defragPeriod := profile.Compaction.Period * time.Duration(len(hosts))
+ for _, h := range hosts {
+ c := connect(clientSet, []string{h})
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+ runDefragLoop(ctx, c, defragPeriod, finish)
+ }(c)
+ }
+ wg.Wait()
+}
+
+func runDefragLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
+ jittered := time.Duration(robustnessrand.RandRange(int64(period-period/2), int64(period+period/2)))
+ ticker := time.NewTicker(jittered)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-finish:
+ return
+ case <-ticker.C:
+ }
+ dctx, cancel := context.WithTimeout(ctx, traffic.RequestTimeout)
+ _, err := c.Defragment(dctx)
+ cancel()
+ if err != nil {
+ continue
+ }
+ }
+}
+
+func connect(cs *client.ClientSet, endpoints []string) *client.RecordingClient {
+ cli, err := cs.NewClient(endpoints)
+ if err != nil {
+ // Antithesis Assertion: client should always be able to connect to an etcd host
+ assert.Unreachable("Client failed to connect to an etcd host", map[string]any{"endpoints": endpoints, "error": err})
+ os.Exit(1)
+ }
+ return cli
+}
+
+func closeAfter(ctx context.Context, t time.Duration) <-chan struct{} {
+ out := make(chan struct{})
+ go func() {
+ select {
+ case <-time.After(t):
+ case <-ctx.Done():
+ }
+ close(out)
+ }()
+ return out
+}
diff --git a/tests/common/alarm_test.go b/tests/common/alarm_test.go
index 67d83ddf5be5..24bb3df1a78d 100644
--- a/tests/common/alarm_test.go
+++ b/tests/common/alarm_test.go
@@ -30,7 +30,7 @@ import (
func TestAlarm(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t,
config.WithClusterSize(1),
@@ -41,12 +41,13 @@ func TestAlarm(t *testing.T) {
testutils.ExecuteUntil(ctx, t, func() {
// test small put still works
smallbuf := strings.Repeat("a", 64)
- require.NoErrorf(t, cc.Put(ctx, "1st_test", smallbuf, config.PutOptions{}), "alarmTest: put kv error")
+ _, err := cc.Put(ctx, "1st_test", smallbuf, config.PutOptions{})
+ require.NoErrorf(t, err, "alarmTest: put kv error")
// write some chunks to fill up the database
buf := strings.Repeat("b", os.Getpagesize())
for {
- if err := cc.Put(ctx, "2nd_test", buf, config.PutOptions{}); err != nil {
+ if _, err = cc.Put(ctx, "2nd_test", buf, config.PutOptions{}); err != nil {
require.ErrorContains(t, err, "etcdserver: mvcc: database space exceeded")
break
}
@@ -57,7 +58,7 @@ func TestAlarm(t *testing.T) {
require.NoErrorf(t, err, "alarmTest: Alarm error")
// check that Put is rejected when alarm is on
- if err = cc.Put(ctx, "3rd_test", smallbuf, config.PutOptions{}); err != nil {
+ if _, err = cc.Put(ctx, "3rd_test", smallbuf, config.PutOptions{}); err != nil {
require.ErrorContains(t, err, "etcdserver: mvcc: database space exceeded")
}
@@ -90,14 +91,14 @@ func TestAlarm(t *testing.T) {
}
// put one more key below quota
- err = cc.Put(ctx, "4th_test", smallbuf, config.PutOptions{})
+ _, err = cc.Put(ctx, "4th_test", smallbuf, config.PutOptions{})
require.NoError(t, err)
})
}
func TestAlarmlistOnMemberRestart(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t,
config.WithClusterSize(1),
diff --git a/tests/common/auth_test.go b/tests/common/auth_test.go
index 2136e0f81d44..a3cf86958308 100644
--- a/tests/common/auth_test.go
+++ b/tests/common/auth_test.go
@@ -36,6 +36,9 @@ var (
tokenTTL = time.Second * 3
defaultAuthToken = fmt.Sprintf("jwt,pub-key=%s,priv-key=%s,sign-method=RS256,ttl=%s",
mustAbsPath("../fixtures/server.crt"), mustAbsPath("../fixtures/server.key.insecure"), tokenTTL)
+ defaultKeyPath = mustAbsPath("../fixtures/server.key.insecure")
+ verifyJWTOnlyAuth = fmt.Sprintf("jwt,pub-key=%s,sign-method=RS256,ttl=%s",
+ mustAbsPath("../fixtures/server.crt"), tokenTTL)
)
const (
@@ -48,7 +51,7 @@ const (
func TestAuthEnable(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -60,25 +63,29 @@ func TestAuthEnable(t *testing.T) {
func TestAuthDisable(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- require.NoError(t, cc.Put(ctx, "hoo", "a", config.PutOptions{}))
+ _, err := cc.Put(ctx, "hoo", "a", config.PutOptions{})
+ require.NoError(t, err)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// test-user doesn't have the permission, it must fail
- require.Error(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.Error(t, err)
require.NoErrorf(t, rootAuthClient.AuthDisable(ctx), "failed to disable auth")
// now ErrAuthNotEnabled of Authenticate() is simply ignored
- require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// now the key can be accessed
- require.NoError(t, cc.Put(ctx, "hoo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err := cc.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
@@ -90,7 +97,7 @@ func TestAuthDisable(t *testing.T) {
func TestAuthGracefulDisable(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -119,7 +126,8 @@ func TestAuthGracefulDisable(t *testing.T) {
}
startedC <- struct{}{}
// the watcher should still work after reconnecting
- assert.NoErrorf(t, rootAuthClient.Put(ctx, "key", "value", config.PutOptions{}), "failed to put key value")
+ _, err := rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
+ assert.NoErrorf(t, err, "failed to put key value")
}()
<-startedC
@@ -140,7 +148,7 @@ func TestAuthGracefulDisable(t *testing.T) {
func TestAuthStatus(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -160,24 +168,27 @@ func TestAuthStatus(t *testing.T) {
func TestAuthRoleUpdate(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
- require.ErrorContains(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}), PermissionDenied)
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.ErrorContains(t, err, PermissionDenied)
// grant a new key
- _, err := rootAuthClient.RoleGrantPermission(ctx, testRoleName, "hoo", "", clientv3.PermissionType(clientv3.PermReadWrite))
+ _, err = rootAuthClient.RoleGrantPermission(ctx, testRoleName, "hoo", "", clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
// try a newly granted key
- require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
@@ -188,7 +199,8 @@ func TestAuthRoleUpdate(t *testing.T) {
_, err = rootAuthClient.RoleRevokePermission(ctx, testRoleName, "hoo", "")
require.NoError(t, err)
// try put to the revoked key
- require.ErrorContains(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}), PermissionDenied)
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.ErrorContains(t, err, PermissionDenied)
// confirm a key still granted can be accessed
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
@@ -200,20 +212,22 @@ func TestAuthRoleUpdate(t *testing.T) {
func TestAuthUserDeleteDuringOps(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// create a key
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
@@ -224,27 +238,29 @@ func TestAuthUserDeleteDuringOps(t *testing.T) {
_, err = rootAuthClient.UserDelete(ctx, testUserName)
require.NoError(t, err)
// check the user is deleted
- err = testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{})
+ _, err = testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{})
require.ErrorContains(t, err, AuthenticationFailed)
})
}
func TestAuthRoleRevokeDuringOps(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- require.NoError(t, cc.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// create a key
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err := testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
@@ -262,7 +278,8 @@ func TestAuthRoleRevokeDuringOps(t *testing.T) {
require.NoError(t, err)
// try a newly granted key
- require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
@@ -273,10 +290,12 @@ func TestAuthRoleRevokeDuringOps(t *testing.T) {
_, err = rootAuthClient.UserRevokeRole(ctx, testUserName, testRoleName)
require.NoError(t, err)
// check the role is revoked and permission is lost from the user
- require.ErrorContains(t, testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{}), PermissionDenied)
+ _, err = testUserAuthClient.Put(ctx, "foo", "baz", config.PutOptions{})
+ require.ErrorContains(t, err, PermissionDenied)
// try a key that can be accessed from the remaining role
- require.NoError(t, testUserAuthClient.Put(ctx, "hoo", "bar2", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "hoo", "bar2", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "hoo", config.GetOptions{})
require.NoError(t, err)
@@ -288,20 +307,22 @@ func TestAuthRoleRevokeDuringOps(t *testing.T) {
func TestAuthWriteKey(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- require.NoError(t, cc.Put(ctx, "foo", "a", config.PutOptions{}))
+ _, err := cc.Put(ctx, "foo", "a", config.PutOptions{})
+ require.NoError(t, err)
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
// confirm root role can access to all keys
- require.NoError(t, rootAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err = rootAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
resp, err := rootAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Lenf(t, resp.Kvs, 1, "want key value pair 'foo' 'bar' but got %+v", resp.Kvs)
@@ -312,7 +333,8 @@ func TestAuthWriteKey(t *testing.T) {
require.ErrorContains(t, err, AuthenticationFailed)
// try good user
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{})
+ require.NoError(t, err)
// confirm put succeeded
resp, err = testUserAuthClient.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
@@ -378,7 +400,7 @@ func TestAuthTxn(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.cfg))
defer clus.Close()
@@ -389,11 +411,11 @@ func TestAuthTxn(t *testing.T) {
// keys with 2 suffix are granted to test-user, see Line 399
grantedKeys := []string{"c2", "s2", "f2"}
for _, key := range keys {
- err := cc.Put(ctx, key, "v", config.PutOptions{})
+ _, err := cc.Put(ctx, key, "v", config.PutOptions{})
require.NoError(t, err)
}
for _, key := range grantedKeys {
- err := cc.Put(ctx, key, "v", config.PutOptions{})
+ _, err := cc.Put(ctx, key, "v", config.PutOptions{})
require.NoError(t, err)
}
@@ -424,7 +446,7 @@ func TestAuthTxn(t *testing.T) {
func TestAuthPrefixPerm(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -440,10 +462,12 @@ func TestAuthPrefixPerm(t *testing.T) {
// try a prefix granted permission
for i := 0; i < 10; i++ {
key := fmt.Sprintf("%s%d", prefix, i)
- require.NoError(t, testUserAuthClient.Put(ctx, key, "val", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, key, "val", config.PutOptions{})
+ require.NoError(t, err)
}
// expect put 'key with prefix end "/prefix0"' value failed
- require.ErrorContains(t, testUserAuthClient.Put(ctx, clientv3.GetPrefixRangeEnd(prefix), "baz", config.PutOptions{}), PermissionDenied)
+ _, err = testUserAuthClient.Put(ctx, clientv3.GetPrefixRangeEnd(prefix), "baz", config.PutOptions{})
+ require.ErrorContains(t, err, PermissionDenied)
// grant the prefix2 keys to test-user
prefix2 := "/prefix2/"
@@ -451,14 +475,15 @@ func TestAuthPrefixPerm(t *testing.T) {
require.NoError(t, err)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("%s%d", prefix2, i)
- require.NoError(t, testUserAuthClient.Put(ctx, key, "val", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, key, "val", config.PutOptions{})
+ require.NoError(t, err)
}
})
}
func TestAuthLeaseKeepAlive(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -470,7 +495,8 @@ func TestAuthLeaseKeepAlive(t *testing.T) {
resp, err := rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
leaseID := resp.ID
- require.NoError(t, rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: leaseID}))
+ _, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: leaseID})
+ require.NoError(t, err)
_, err = rootAuthClient.KeepAliveOnce(ctx, leaseID)
require.NoError(t, err)
@@ -484,7 +510,7 @@ func TestAuthLeaseKeepAlive(t *testing.T) {
func TestAuthRevokeWithDelete(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -515,7 +541,7 @@ func TestAuthRevokeWithDelete(t *testing.T) {
func TestAuthLeaseTimeToLiveExpired(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -526,7 +552,8 @@ func TestAuthLeaseTimeToLiveExpired(t *testing.T) {
resp, err := rootAuthClient.Grant(ctx, 2)
require.NoError(t, err)
leaseID := resp.ID
- require.NoError(t, rootAuthClient.Put(ctx, "key", "val", config.PutOptions{LeaseID: leaseID}))
+ _, err = rootAuthClient.Put(ctx, "key", "val", config.PutOptions{LeaseID: leaseID})
+ require.NoError(t, err)
// eliminate false positive
time.Sleep(3 * time.Second)
tresp, err := rootAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{})
@@ -553,7 +580,7 @@ func TestAuthLeaseGrantLeases(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -586,7 +613,7 @@ func TestAuthLeaseGrantLeases(t *testing.T) {
func TestAuthMemberAdd(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -615,10 +642,10 @@ func TestAuthCompact(t *testing.T) {
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
- err := rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
+ _, err := rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
require.NoError(t, err)
- err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
+ _, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{})
require.NoError(t, err)
_, err = testUserAuthClient.Compact(ctx, 1, config.CompactOption{Physical: true})
@@ -647,14 +674,14 @@ func TestAuthLeaseLeases(t *testing.T) {
require.NoError(t, err)
firstLeaseID := lresp.ID
- err = rootAuthClient.Put(ctx, "foo", "value", config.PutOptions{LeaseID: firstLeaseID})
+ _, err = rootAuthClient.Put(ctx, "foo", "value", config.PutOptions{LeaseID: firstLeaseID})
require.NoError(t, err)
lresp, err = rootAuthClient.Grant(ctx, 90)
require.NoError(t, err)
secondLeaseID := lresp.ID
- err = rootAuthClient.Put(ctx, "foo1", "value", config.PutOptions{LeaseID: secondLeaseID})
+ _, err = rootAuthClient.Put(ctx, "foo1", "value", config.PutOptions{LeaseID: secondLeaseID})
require.NoError(t, err)
_, err = testUserAuthClient.Leases(ctx)
@@ -707,7 +734,7 @@ func TestAuthMemberList(t *testing.T) {
func TestAuthMemberRemove(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clusterSize := 3
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: clusterSize}))
@@ -747,7 +774,7 @@ func TestAuthMemberRemove(t *testing.T) {
func TestAuthTestInvalidMgmt(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -764,7 +791,7 @@ func TestAuthTestInvalidMgmt(t *testing.T) {
func TestAuthLeaseRevoke(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -775,7 +802,7 @@ func TestAuthLeaseRevoke(t *testing.T) {
lresp, err := rootAuthClient.Grant(ctx, 10)
require.NoError(t, err)
- err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: lresp.ID})
+ _, err = rootAuthClient.Put(ctx, "key", "value", config.PutOptions{LeaseID: lresp.ID})
require.NoError(t, err)
_, err = rootAuthClient.Revoke(ctx, lresp.ID)
@@ -795,7 +822,7 @@ func TestAuthLeaseRevoke(t *testing.T) {
func TestAuthRoleGet(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -821,7 +848,7 @@ func TestAuthRoleGet(t *testing.T) {
func TestAuthUserGet(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -847,7 +874,7 @@ func TestAuthUserGet(t *testing.T) {
func TestAuthRoleList(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1}))
defer clus.Close()
@@ -864,7 +891,7 @@ func TestAuthRoleList(t *testing.T) {
func TestAuthJWTExpire(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
@@ -873,20 +900,42 @@ func TestAuthJWTExpire(t *testing.T) {
require.NoErrorf(t, setupAuth(cc, []authRole{testRole}, []authUser{rootUser, testUser}), "failed to enable auth")
testUserAuthClient := testutils.MustClient(clus.Client(WithAuth(testUserName, testPassword)))
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// wait an expiration of my JWT token
<-time.After(3 * tokenTTL)
// e2e test will generate a new token while
// integration test that re-uses the same etcd client will refresh the token on server failure.
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
+ })
+}
+
+func TestAuthJWTOnly(t *testing.T) {
+ testRunner.BeforeTest(t)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
+ defer cancel()
+ clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: verifyJWTOnlyAuth}))
+ defer clus.Close()
+ cc := testutils.MustClient(clus.Client())
+ testutils.ExecuteUntil(ctx, t, func() {
+ authRev, err := setupAuthAndGetRevision(cc, []authRole{testRole}, []authUser{rootUser, testUser})
+ require.NoErrorf(t, err, "failed to enable auth")
+
+ token, err := createSignedJWT(defaultKeyPath, "RS256", testUserName, authRev)
+ require.NoErrorf(t, err, "failed to create test user JWT")
+
+ testUserAuthClient := testutils.MustClient(clus.Client(WithAuthToken(token)))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
})
}
// TestAuthRevisionConsistency ensures auth revision is the same after member restarts
func TestAuthRevisionConsistency(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
@@ -922,7 +971,7 @@ func TestAuthRevisionConsistency(t *testing.T) {
// TestAuthTestCacheReload ensures permissions are persisted and will be reloaded after member restarts
func TestAuthTestCacheReload(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
@@ -933,21 +982,23 @@ func TestAuthTestCacheReload(t *testing.T) {
// create foo since that is within the permission set
// expectation is to succeed
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
// restart the node
clus.Members()[0].Stop()
require.NoError(t, clus.Members()[0].Start(ctx))
// nothing has changed, but it fails without refreshing cache after restart
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar2", config.PutOptions{})
+ require.NoError(t, err)
})
}
// TestAuthLeaseTimeToLive gated lease time to live with RBAC control
func TestAuthLeaseTimeToLive(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(config.ClusterConfig{ClusterSize: 1, AuthToken: defaultAuthToken}))
defer clus.Close()
@@ -961,7 +1012,8 @@ func TestAuthLeaseTimeToLive(t *testing.T) {
require.NoError(t, err)
leaseID := gresp.ID
- require.NoError(t, testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseID}))
+ _, err = testUserAuthClient.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseID})
+ require.NoError(t, err)
_, err = testUserAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: true})
require.NoError(t, err)
@@ -972,7 +1024,8 @@ func TestAuthLeaseTimeToLive(t *testing.T) {
require.ErrorContains(t, err, "etcdserver: user name is empty")
rootAuthClient := testutils.MustClient(clus.Client(WithAuth(rootUserName, rootPassword)))
- require.NoError(t, rootAuthClient.Put(ctx, "bar", "foo", config.PutOptions{LeaseID: leaseID}))
+ _, err = rootAuthClient.Put(ctx, "bar", "foo", config.PutOptions{LeaseID: leaseID})
+ require.NoError(t, err)
// the lease is attached to bar, which test-user cannot access
_, err = testUserAuthClient.TimeToLive(ctx, leaseID, config.LeaseOption{WithAttachedKeys: true})
@@ -1002,7 +1055,7 @@ func TestAuthAlarm(t *testing.T) {
anonAuthClient := testutils.MustClient(clus.Client())
for i := 0; ; i++ {
- err := rootAuthClient.Put(ctx,
+ _, err := rootAuthClient.Put(ctx,
testutil.PickKey(int64(i)), strings.Repeat("A", 1024), config.PutOptions{})
if err == nil {
continue
diff --git a/tests/common/auth_util.go b/tests/common/auth_util.go
index b157ef4fc83b..6b347f9799de 100644
--- a/tests/common/auth_util.go
+++ b/tests/common/auth_util.go
@@ -17,8 +17,11 @@ package common
import (
"context"
"fmt"
+ "os"
"testing"
+ "time"
+ "github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/authpb"
@@ -93,6 +96,29 @@ func createUsers(c interfaces.Client, users []authUser) error {
return nil
}
+func createSignedJWT(keyPath, alg, username string, authRevision uint64) (string, error) {
+ signMethod := jwt.GetSigningMethod(alg)
+
+ keyBytes, err := os.ReadFile(keyPath)
+ if err != nil {
+ return "", err
+ }
+
+ key, err := jwt.ParseRSAPrivateKeyFromPEM(keyBytes)
+ if err != nil {
+ return "", err
+ }
+
+ tk := jwt.NewWithClaims(signMethod,
+ jwt.MapClaims{
+ "username": username,
+ "revision": authRevision,
+ "exp": time.Now().Add(time.Minute).Unix(),
+ })
+
+ return tk.SignedString(key)
+}
+
func setupAuth(c interfaces.Client, roles []authRole, users []authUser) error {
// create roles
if err := createRoles(c, roles); err != nil {
@@ -107,6 +133,29 @@ func setupAuth(c interfaces.Client, roles []authRole, users []authUser) error {
return c.AuthEnable(context.TODO())
}
+func setupAuthAndGetRevision(c interfaces.Client, roles []authRole, users []authUser) (uint64, error) {
+ // create roles
+ if err := createRoles(c, roles); err != nil {
+ return 0, err
+ }
+
+ if err := createUsers(c, users); err != nil {
+ return 0, err
+ }
+
+ // This needs to happen before enabling auth for the TestAuthJWTOnly
+ // test case because once auth is enabled we can no longer mint a valid
+ // auth token without the revision, which we won't be able to obtain
+ // without a valid auth token.
+ authrev, err := c.AuthStatus(context.TODO())
+ if err != nil {
+ return 0, err
+ }
+
+ // enable auth
+ return authrev.AuthRevision, c.AuthEnable(context.TODO())
+}
+
func requireRolePermissionEqual(t *testing.T, expectRole authRole, actual []*authpb.Permission) {
require.Len(t, actual, 1)
require.Equal(t, expectRole.permission, clientv3.PermissionType(actual[0].PermType))
diff --git a/tests/common/compact_test.go b/tests/common/compact_test.go
index 412c46215507..842b54dd8eb3 100644
--- a/tests/common/compact_test.go
+++ b/tests/common/compact_test.go
@@ -43,7 +43,7 @@ func TestCompact(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
defer clus.Close()
@@ -51,7 +51,8 @@ func TestCompact(t *testing.T) {
testutils.ExecuteUntil(ctx, t, func() {
kvs := []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}}
for i := range kvs {
- require.NoErrorf(t, cc.Put(ctx, kvs[i].Key, kvs[i].Val, config.PutOptions{}), "compactTest #%d: put kv error", i)
+ _, err := cc.Put(ctx, kvs[i].Key, kvs[i].Val, config.PutOptions{})
+ require.NoErrorf(t, err, "compactTest #%d: put kv error", i)
}
get, err := cc.Get(ctx, "key", config.GetOptions{Revision: 3})
require.NoErrorf(t, err, "compactTest: Get kv by revision error")
diff --git a/tests/common/defrag_test.go b/tests/common/defrag_test.go
index d0af0e68c4a2..6238ae37fc68 100644
--- a/tests/common/defrag_test.go
+++ b/tests/common/defrag_test.go
@@ -27,7 +27,7 @@ import (
func TestDefragOnline(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
options := config.DefragOption{Timeout: 10 * time.Second}
clus := testRunner.NewCluster(ctx, t)
@@ -36,7 +36,8 @@ func TestDefragOnline(t *testing.T) {
defer clus.Close()
kvs := []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}}
for i := range kvs {
- require.NoErrorf(t, cc.Put(ctx, kvs[i].Key, kvs[i].Val, config.PutOptions{}), "compactTest #%d: put kv error", i)
+ _, err := cc.Put(ctx, kvs[i].Key, kvs[i].Val, config.PutOptions{})
+ require.NoErrorf(t, err, "compactTest #%d: put kv error", i)
}
_, err := cc.Compact(ctx, 4, config.CompactOption{Physical: true, Timeout: 10 * time.Second})
require.NoErrorf(t, err, "defrag_test: compact with revision error (%v)", err)
diff --git a/tests/common/e2e_test.go b/tests/common/e2e_test.go
index 11c4f94a335b..2844bcbf31ef 100644
--- a/tests/common/e2e_test.go
+++ b/tests/common/e2e_test.go
@@ -17,6 +17,11 @@
package common
import (
+ "fmt"
+ "os"
+ "strconv"
+ "time"
+
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/tests/v3/framework"
"go.etcd.io/etcd/tests/v3/framework/config"
@@ -28,49 +33,70 @@ func init() {
clusterTestCases = e2eClusterTestCases
}
+const (
+ // minimalE2eEnabledEnvVarName is for reducing e2e test matrix, leading to faster CI runtimes in some cases(e.g., presubmits).
+ // See https://github.com/etcd-io/etcd/issues/18983 for background.
+ minimalE2eEnabledEnvVarName = "E2E_TEST_MINIMAL"
+)
+
+func minimalE2eEnabled() bool {
+ v, ok := os.LookupEnv(minimalE2eEnabledEnvVarName)
+ if !ok {
+ return false
+ }
+ parsed, err := strconv.ParseBool(v)
+ if err != nil {
+ fmt.Printf("Invalid %s value %q: %v\n", minimalE2eEnabledEnvVarName, v, err)
+ return false
+ }
+ return parsed
+}
+
func e2eClusterTestCases() []testCase {
- tcs := []testCase{
+ minimalTestCases := []testCase{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
- name: "PeerTLS",
- config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
- },
- {
- name: "PeerAutoTLS",
- config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
- },
- {
- name: "ClientTLS",
- config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
- },
- {
- name: "ClientAutoTLS",
- config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
+ name: "PeerTLS and ClientTLS",
+ config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS, ClientTLS: config.ManualTLS},
},
}
+ if minimalE2eEnabled() {
+ return minimalTestCases
+ }
+
+ tcs := append(minimalTestCases,
+ testCase{
+ name: "PeerAutoTLS and ClientAutoTLS",
+ config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS, ClientTLS: config.AutoTLS},
+ },
+ )
+
if fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- tcs = append(tcs, testCase{
- name: "MinorityLastVersion",
- config: config.ClusterConfig{
- ClusterSize: 3,
- ClusterContext: &e2e.ClusterContext{
- Version: e2e.MinorityLastVersion,
+ tcs = append(tcs,
+ testCase{
+ name: "MinorityLastVersion",
+ config: config.ClusterConfig{
+ ClusterSize: 3,
+ ClusterContext: &e2e.ClusterContext{
+ Version: e2e.MinorityLastVersion,
+ },
},
},
- }, testCase{
- name: "QuorumLastVersion",
- config: config.ClusterConfig{
- ClusterSize: 3,
- ClusterContext: &e2e.ClusterContext{
- Version: e2e.QuorumLastVersion,
+ testCase{
+ name: "QuorumLastVersion",
+ config: config.ClusterConfig{
+ ClusterSize: 3,
+ ClusterContext: &e2e.ClusterContext{
+ Version: e2e.QuorumLastVersion,
+ },
},
- },
- })
+ })
}
+
return tcs
}
@@ -78,6 +104,26 @@ func WithAuth(userName, password string) config.ClientOption {
return e2e.WithAuth(userName, password)
}
+func WithAuthToken(token string) config.ClientOption {
+ return e2e.WithAuthToken(token)
+}
+
func WithEndpoints(endpoints []string) config.ClientOption {
return e2e.WithEndpoints(endpoints)
}
+
+func WithDialTimeout(tio time.Duration) config.ClientOption {
+ return e2e.WithDialTimeout(tio)
+}
+
+func WithHTTP2Debug() config.ClusterOption {
+ return e2e.WithHTTP2Debug()
+}
+
+func WithUnixClient() config.ClusterOption {
+ return e2e.WithUnixClient()
+}
+
+func WithTCPClient() config.ClusterOption {
+ return e2e.WithTCPClient()
+}
diff --git a/tests/common/endpoint_test.go b/tests/common/endpoint_test.go
index 570b59d7f171..03074595b572 100644
--- a/tests/common/endpoint_test.go
+++ b/tests/common/endpoint_test.go
@@ -28,7 +28,7 @@ import (
func TestEndpointStatus(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
defer clus.Close()
@@ -41,7 +41,7 @@ func TestEndpointStatus(t *testing.T) {
func TestEndpointHashKV(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
defer clus.Close()
@@ -51,7 +51,8 @@ func TestEndpointHashKV(t *testing.T) {
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key-%d", i)
value := fmt.Sprintf("value-%d", i)
- require.NoErrorf(t, cc.Put(ctx, key, value, config.PutOptions{}), "count not put key %q", key)
+ _, err := cc.Put(ctx, key, value, config.PutOptions{})
+ require.NoErrorf(t, err, "count not put key %q", key)
}
t.Log("Check all members' Hash and HashRevision")
@@ -72,7 +73,7 @@ func TestEndpointHashKV(t *testing.T) {
func TestEndpointHealth(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
defer clus.Close()
diff --git a/tests/e2e/ctl_v3_grpc_test.go b/tests/common/grpc_test.go
similarity index 59%
rename from tests/e2e/ctl_v3_grpc_test.go
rename to tests/common/grpc_test.go
index 78881f3ed056..496bf4868c40 100644
--- a/tests/e2e/ctl_v3_grpc_test.go
+++ b/tests/common/grpc_test.go
@@ -12,31 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//go:build !cluster_proxy
-
-package e2e
+package common
import (
"context"
"fmt"
- "net/url"
- "strings"
"testing"
"time"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "go.etcd.io/etcd/pkg/v3/expect"
"go.etcd.io/etcd/tests/v3/framework/config"
- "go.etcd.io/etcd/tests/v3/framework/e2e"
+ intf "go.etcd.io/etcd/tests/v3/framework/interfaces"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
+type clientSocket int
+
+const (
+ clientSocketTCP clientSocket = iota
+ clientSocketUnix
+)
+
func TestAuthority(t *testing.T) {
+ testRunner.BeforeTest(t)
tcs := []struct {
name string
- useUnix bool
+ clientSocket clientSocket
useTLS bool
useInsecureTLS bool
clientURLPattern string
@@ -44,43 +46,46 @@ func TestAuthority(t *testing.T) {
}{
{
name: "unix:path",
- useUnix: true,
+ clientSocket: clientSocketUnix,
clientURLPattern: "unix:localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
{
name: "unix://absolute_path",
- useUnix: true,
+ clientSocket: clientSocketUnix,
clientURLPattern: "unix://localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
// "unixs" is not standard schema supported by etcd
{
name: "unixs:absolute_path",
- useUnix: true,
+ clientSocket: clientSocketUnix,
useTLS: true,
clientURLPattern: "unixs:localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
{
name: "unixs://absolute_path",
- useUnix: true,
+ clientSocket: clientSocketUnix,
useTLS: true,
clientURLPattern: "unixs://localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
{
name: "http://domain[:port]",
+ clientSocket: clientSocketTCP,
clientURLPattern: "http://localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
{
name: "http://address[:port]",
+ clientSocket: clientSocketTCP,
clientURLPattern: "http://127.0.0.1:${MEMBER_PORT}",
expectAuthorityPattern: "127.0.0.1:${MEMBER_PORT}",
},
{
name: "https://domain[:port] insecure",
+ clientSocket: clientSocketTCP,
useTLS: true,
useInsecureTLS: true,
clientURLPattern: "https://localhost:${MEMBER_PORT}",
@@ -88,6 +93,7 @@ func TestAuthority(t *testing.T) {
},
{
name: "https://address[:port] insecure",
+ clientSocket: clientSocketTCP,
useTLS: true,
useInsecureTLS: true,
clientURLPattern: "https://127.0.0.1:${MEMBER_PORT}",
@@ -95,12 +101,14 @@ func TestAuthority(t *testing.T) {
},
{
name: "https://domain[:port]",
+ clientSocket: clientSocketTCP,
useTLS: true,
clientURLPattern: "https://localhost:${MEMBER_PORT}",
expectAuthorityPattern: "localhost:${MEMBER_PORT}",
},
{
name: "https://address[:port]",
+ clientSocket: clientSocketTCP,
useTLS: true,
clientURLPattern: "https://127.0.0.1:${MEMBER_PORT}",
expectAuthorityPattern: "127.0.0.1:${MEMBER_PORT}",
@@ -109,64 +117,54 @@ func TestAuthority(t *testing.T) {
for _, tc := range tcs {
for _, clusterSize := range []int{1, 3} {
t.Run(fmt.Sprintf("Size: %d, Scenario: %q", clusterSize, tc.name), func(t *testing.T) {
- e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
- cfg := e2e.NewConfigNoTLS()
+ cfg := config.NewClusterConfig()
cfg.ClusterSize = clusterSize
- if tc.useTLS {
- cfg.Client.ConnectionType = e2e.ClientTLS
+
+ switch {
+ case tc.useInsecureTLS:
+ cfg.ClientTLS = config.AutoTLS
+ case tc.useTLS:
+ cfg.ClientTLS = config.ManualTLS
+ default:
+ cfg.ClientTLS = config.NoTLS
}
- cfg.Client.AutoTLS = tc.useInsecureTLS
- // Enable debug mode to get logs with http2 headers (including authority)
- cfg.EnvVars = map[string]string{"GODEBUG": "http2debug=2"}
- if tc.useUnix {
- cfg.BaseClientScheme = "unix"
+
+ opts := []config.ClusterOption{
+ config.WithClusterConfig(cfg),
+ WithHTTP2Debug(), // enable http2 header logs only for e2e tests
}
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
- if err != nil {
- t.Fatalf("could not start etcd process cluster (%v)", err)
+ switch tc.clientSocket {
+ case clientSocketTCP:
+ opts = append(opts, WithTCPClient())
+ case clientSocketUnix:
+ opts = append(opts, WithUnixClient())
}
- defer epc.Close()
- endpoints := templateEndpoints(t, tc.clientURLPattern, epc)
- client, err := e2e.NewEtcdctl(cfg.Client, endpoints)
- require.NoError(t, err)
+ clus := testRunner.NewCluster(ctx, t, opts...)
+ defer clus.Close()
+
+ tmpEndpoints, ok := clus.(intf.TemplateEndpoints)
+ require.Truef(t, ok, "cluster does not implement TemplateEndpoints")
+
+ endpoints := tmpEndpoints.TemplateEndpoints(t, tc.clientURLPattern)
+
+ cc := testutils.MustClient(clus.Client(WithEndpoints(endpoints)))
+
for i := 0; i < 100; i++ {
- require.NoError(t, client.Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := cc.Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
}
testutils.ExecuteWithTimeout(t, 5*time.Second, func() {
- assertAuthority(t, tc.expectAuthorityPattern, epc)
+ asserter, ok := clus.(intf.AssertAuthority)
+ require.Truef(t, ok, "cluster does not implement AssertAuthority")
+ asserter.AssertAuthority(t, tc.expectAuthorityPattern)
})
})
}
}
}
-
-func templateEndpoints(t *testing.T, pattern string, clus *e2e.EtcdProcessCluster) []string {
- t.Helper()
- var endpoints []string
- for i := 0; i < clus.Cfg.ClusterSize; i++ {
- ent := pattern
- ent = strings.ReplaceAll(ent, "${MEMBER_PORT}", fmt.Sprintf("%d", e2e.EtcdProcessBasePort+i*5))
- endpoints = append(endpoints, ent)
- }
- return endpoints
-}
-
-func assertAuthority(t *testing.T, expectAuthorityPattern string, clus *e2e.EtcdProcessCluster) {
- for i := range clus.Procs {
- line, _ := clus.Procs[i].Logs().ExpectWithContext(context.TODO(), expect.ExpectedResponse{Value: `http2: decoded hpack field header field ":authority"`})
- line = strings.TrimSuffix(line, "\n")
- line = strings.TrimSuffix(line, "\r")
-
- u, err := url.Parse(clus.Procs[i].EndpointsGRPC()[0])
- require.NoError(t, err)
- expectAuthority := strings.ReplaceAll(expectAuthorityPattern, "${MEMBER_PORT}", u.Port())
- expectLine := fmt.Sprintf(`http2: decoded hpack field header field ":authority" = %q`, expectAuthority)
- assert.Truef(t, strings.HasSuffix(line, expectLine), "Got %q expected suffix %q", line, expectLine)
- }
-}
diff --git a/tests/common/hashkv_test.go b/tests/common/hashkv_test.go
new file mode 100644
index 000000000000..56229b4fdf9d
--- /dev/null
+++ b/tests/common/hashkv_test.go
@@ -0,0 +1,260 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 common
+
+import (
+ "context"
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "go.etcd.io/etcd/tests/v3/framework/config"
+ intf "go.etcd.io/etcd/tests/v3/framework/interfaces"
+ "go.etcd.io/etcd/tests/v3/framework/testutils"
+)
+
+// TestVerifyHashKVAfterCompact tests that HashKV is consistent across all members after a physical compaction.
+// It tests both cases where the compaction is on a tombstone revision and where it is not.
+func TestVerifyHashKVAfterCompact(t *testing.T) {
+ testRunner.BeforeTest(t)
+ for _, keys := range [][]string{
+ {"key0"},
+ {"key0", "key1"},
+ } {
+ for _, compactedOnTombstoneRev := range []bool{false, true} {
+ t.Run(fmt.Sprintf("Keys=%v/CompactedOnTombstone=%v", keys, compactedOnTombstoneRev), func(t *testing.T) {
+ for _, tc := range clusterTestCases() {
+ t.Run(tc.name, func(t *testing.T) {
+ if tc.config.ClusterSize < 2 {
+ t.Skip("Skipping test for single-member cluster")
+ }
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
+ defer cancel()
+
+ clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
+ defer clus.Close()
+
+ cc := testutils.MustClient(clus.Client())
+ tombstoneRevs, latestRev := populateDataForHashKV(t, cc, keys)
+
+ compactedOnRev := tombstoneRevs[0]
+
+ // If compaction revision isn't a tombstone, select a revision in the middle of two tombstones.
+ if !compactedOnTombstoneRev {
+ require.Greater(t, len(tombstoneRevs), 1)
+ compactedOnRev = (tombstoneRevs[0] + tombstoneRevs[1]) / 2
+ require.Greater(t, compactedOnRev, tombstoneRevs[0])
+ require.Greater(t, tombstoneRevs[1], compactedOnRev)
+ }
+
+ _, err := cc.Compact(ctx, compactedOnRev, config.CompactOption{Physical: true})
+ require.NoError(t, err)
+
+ for rev := compactedOnRev; rev <= latestRev; rev++ {
+ verifyConsistentHashKVAcrossAllMembers(t, cc, rev)
+ }
+ })
+ }
+ })
+ }
+ }
+}
+
+// TestVerifyHashKVAfterTwoCompactsOnTombstone tests that HashKV is consistent
+// across all members after two physical compactions on tombstone revisions.
+func TestVerifyHashKVAfterTwoCompactsOnTombstone(t *testing.T) {
+ testRunner.BeforeTest(t)
+ for _, tc := range clusterTestCases() {
+ t.Run(tc.name, func(t *testing.T) {
+ if tc.config.ClusterSize < 2 {
+ t.Skip("Skipping test for single-member cluster")
+ }
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
+ defer cancel()
+
+ clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
+ defer clus.Close()
+
+ cc := testutils.MustClient(clus.Client())
+ tombstoneRevs, latestRev := populateDataForHashKV(t, cc, []string{"key0"})
+ require.GreaterOrEqual(t, len(tombstoneRevs), 2)
+
+ firstCompactOnRev := tombstoneRevs[0]
+ t.Logf("COMPACT rev=%d", firstCompactOnRev)
+ _, err := cc.Compact(ctx, firstCompactOnRev, config.CompactOption{Physical: true})
+ require.NoError(t, err)
+
+ secondCompactOnRev := tombstoneRevs[1]
+ t.Logf("COMPACT rev=%d", secondCompactOnRev)
+ _, err = cc.Compact(ctx, secondCompactOnRev, config.CompactOption{Physical: true})
+ require.NoError(t, err)
+
+ for rev := secondCompactOnRev; rev <= latestRev; rev++ {
+ verifyConsistentHashKVAcrossAllMembers(t, cc, rev)
+ }
+ })
+ }
+}
+
+// TestVerifyHashKVAfterCompactOnLastTombstone tests that HashKV is consistent
+// across all members after a physical compaction on the last tombstone revision.
+func TestVerifyHashKVAfterCompactOnLastTombstone(t *testing.T) {
+ testRunner.BeforeTest(t)
+
+ for _, keys := range [][]string{
+ {"key0"},
+ {"key0", "key1"},
+ } {
+ t.Run(fmt.Sprintf("Keys=%v", keys), func(t *testing.T) {
+ for _, tc := range clusterTestCases() {
+ t.Run(tc.name, func(t *testing.T) {
+ if tc.config.ClusterSize < 2 {
+ t.Skip("Skipping test for single-member cluster")
+ }
+
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
+ defer cancel()
+
+ clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
+ defer clus.Close()
+
+ cc := testutils.MustClient(clus.Client())
+ tombstoneRevs, latestRev := populateDataForHashKV(t, cc, keys)
+ require.NotEmpty(t, tombstoneRevs)
+
+ compactOnRev := tombstoneRevs[len(tombstoneRevs)-1]
+ t.Logf("COMPACT rev=%d", compactOnRev)
+ _, err := cc.Compact(ctx, compactOnRev, config.CompactOption{Physical: true})
+ require.NoError(t, err)
+
+ for rev := compactOnRev; rev <= latestRev; rev++ {
+ verifyConsistentHashKVAcrossAllMembers(t, cc, rev)
+ }
+ })
+ }
+ })
+ }
+}
+
+// TestVerifyHashKVAfterCompactAndDefrag tests that HashKV is consistent
+// within a member before and after a physical compaction and defragmentation.
+func TestVerifyHashKVAfterCompactAndDefrag(t *testing.T) {
+ testRunner.BeforeTest(t)
+
+ for _, tc := range clusterTestCases() {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
+ defer cancel()
+
+ clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
+ defer clus.Close()
+
+ cc := testutils.MustClient(clus.Client())
+ tombstoneRevs, _ := populateDataForHashKV(t, cc, []string{"key0"})
+ require.NotEmpty(t, tombstoneRevs)
+
+ compactOnRev := tombstoneRevs[0]
+ t.Logf("COMPACT rev=%d", compactOnRev)
+
+ before, err := cc.HashKV(ctx, compactOnRev)
+ require.NoError(t, err)
+
+ _, err = cc.Compact(ctx, compactOnRev, config.CompactOption{Physical: true})
+ require.NoError(t, err)
+
+ err = cc.Defragment(ctx, config.DefragOption{})
+ require.NoError(t, err)
+
+ after, err := cc.HashKV(ctx, compactOnRev)
+ require.NoError(t, err)
+
+ require.Len(t, before, len(after))
+ for i := range before {
+ assert.Equal(t, before[i].Hash, after[i].Hash)
+ }
+ })
+ }
+}
+
+// populateDataForHashKV populates some sample data, and return a slice of tombstone
+// revisions and the latest revision.
+func populateDataForHashKV(t *testing.T, cc intf.Client, keys []string) ([]int64, int64) {
+ t.Helper()
+ ctx := t.Context()
+ totalOperations := 40
+
+ var (
+ tombStoneRevs []int64
+ latestRev int64
+ )
+
+ deleteStep := 10 // submit a delete operation on every 10 operations
+ for i := 1; i <= totalOperations; i++ {
+ if i%deleteStep == 0 {
+ t.Logf("Deleting key=%s", keys[0]) // Only delete the first key for simplicity
+ resp, derr := cc.Delete(ctx, keys[0], config.DeleteOptions{})
+ require.NoError(t, derr)
+ latestRev = resp.Header.Revision
+ tombStoneRevs = append(tombStoneRevs, resp.Header.Revision)
+ continue
+ }
+
+ value := fmt.Sprintf("%d", i)
+ var ops []string
+ for _, key := range keys {
+ ops = append(ops, fmt.Sprintf("put %s %s", key, value))
+ }
+ t.Logf("Writing keys: %v, value: %s", keys, value)
+ resp, terr := cc.Txn(ctx, nil, ops, nil, config.TxnOptions{Interactive: true})
+ require.NoError(t, terr)
+ require.True(t, resp.Succeeded)
+ require.Len(t, resp.Responses, len(ops))
+ latestRev = resp.Header.Revision
+ }
+ return tombStoneRevs, latestRev
+}
+
+func verifyConsistentHashKVAcrossAllMembers(t *testing.T, cc intf.Client, hashKVOnRev int64) {
+ t.Helper()
+ ctx := t.Context()
+
+ t.Logf("HashKV on rev=%d", hashKVOnRev)
+
+ assert.Eventually(t, func() bool {
+ resp, err := cc.HashKV(ctx, hashKVOnRev)
+ if err != nil {
+ t.Logf("HashKV failed: %v", err)
+ return false
+ }
+
+ // Ensure that there are multiple members in the cluster.
+ if len(resp) <= 1 {
+ t.Logf("Expected multiple members, got %d", len(resp))
+ return false
+ }
+
+ // Check if all members have the same hash
+ for i := 1; i < len(resp); i++ {
+ if resp[i].Hash != resp[0].Hash {
+ t.Logf("There is a chance that the physical compaction is not yet completed on the followers: Leader hash=%d, Follower hash=%d", resp[0].Hash, resp[i].Hash)
+ return false
+ }
+ }
+ return true
+ }, 3*time.Second, 100*time.Millisecond)
+}
diff --git a/tests/common/integration_test.go b/tests/common/integration_test.go
index c4cabeeb1f98..53234cd04bc3 100644
--- a/tests/common/integration_test.go
+++ b/tests/common/integration_test.go
@@ -34,20 +34,12 @@ func integrationClusterTestCases() []testCase {
config: config.ClusterConfig{ClusterSize: 1},
},
{
- name: "PeerTLS",
- config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
+ name: "PeerTLS and ClientTLS",
+ config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS, ClientTLS: config.ManualTLS},
},
{
- name: "PeerAutoTLS",
- config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
- },
- {
- name: "ClientTLS",
- config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
- },
- {
- name: "ClientAutoTLS",
- config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
+ name: "PeerAutoTLS and ClientAutoTLS",
+ config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS, ClientTLS: config.AutoTLS},
},
}
}
@@ -56,6 +48,22 @@ func WithAuth(userName, password string) config.ClientOption {
return integration.WithAuth(userName, password)
}
+func WithAuthToken(token string) config.ClientOption {
+ return integration.WithAuthToken(token)
+}
+
func WithEndpoints(endpoints []string) config.ClientOption {
return integration.WithEndpoints(endpoints)
}
+
+func WithHTTP2Debug() config.ClusterOption {
+ return integration.WithHTTP2Debug()
+}
+
+func WithUnixClient() config.ClusterOption {
+ return integration.WithUnixClient()
+}
+
+func WithTCPClient() config.ClusterOption {
+ return integration.WithTCPClient()
+}
diff --git a/tests/common/kv_test.go b/tests/common/kv_test.go
index 593ea1e19a0b..67beb9d9e452 100644
--- a/tests/common/kv_test.go
+++ b/tests/common/kv_test.go
@@ -16,14 +16,23 @@ package common
import (
"context"
+ "fmt"
+ "slices"
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/testing/protocmp"
+ "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/server/v3/etcdserver/txn"
"go.etcd.io/etcd/tests/v3/framework/config"
+ "go.etcd.io/etcd/tests/v3/framework/interfaces"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
@@ -31,7 +40,7 @@ func TestKVPut(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -40,7 +49,8 @@ func TestKVPut(t *testing.T) {
testutils.ExecuteUntil(ctx, t, func() {
key, value := "foo", "bar"
- require.NoErrorf(t, cc.Put(ctx, key, value, config.PutOptions{}), "count not put key %q", key)
+ _, err := cc.Put(ctx, key, value, config.PutOptions{})
+ require.NoErrorf(t, err, "count not put key %q", key)
resp, err := cc.Get(ctx, key, config.GetOptions{})
require.NoErrorf(t, err, "count not get key %q, err: %s", key, err)
assert.Lenf(t, resp.Kvs, 1, "Unexpected length of response, got %d", len(resp.Kvs))
@@ -52,66 +62,203 @@ func TestKVPut(t *testing.T) {
}
func TestKVGet(t *testing.T) {
+ testKVGet(t, false)
+}
+
+func TestKVGetStream(t *testing.T) {
+ testKVGet(t, true)
+}
+
+func testKVGet(t *testing.T, stream bool) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
+ if stream && !clusterSupportsGetStream(ctx, t, clus) {
+ t.Skip("RangeStream is not supported by this cluster")
+ }
+
testutils.ExecuteUntil(ctx, t, func() {
- var (
- kvs = []string{"a", "b", "c", "c", "c", "foo", "foo/abc", "fop"}
- wantKvs = []string{"a", "b", "c", "foo", "foo/abc", "fop"}
- kvsByVersion = []string{"a", "b", "foo", "foo/abc", "fop", "c"}
- reversedKvs = []string{"fop", "foo/abc", "foo", "c", "b", "a"}
- )
-
- for i := range kvs {
- require.NoErrorf(t, cc.Put(ctx, kvs[i], "bar", config.PutOptions{}), "count not put key %q", kvs[i])
+ resp, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
+ require.NoError(t, err)
+ firstRev := resp.Header.Revision
+
+ kvA := createKV("a", "aa1", firstRev+1, firstRev+1, 1)
+ kvB := createKV("b", "a", firstRev+2, firstRev+2, 1)
+ kvCV1 := createKV("c", "ac1", firstRev+3, firstRev+3, 1)
+ kvCV2 := createKV("c", "ac2", firstRev+3, firstRev+4, 2)
+ kvC := createKV("c", "aac", firstRev+3, firstRev+5, 3)
+ kvFoo := createKV("foo", "bar", firstRev+6, firstRev+6, 1)
+ kvFooAbc := createKV("foo/abc", "0", firstRev+7, firstRev+7, 1)
+ kvFop := createKV("fop", "s", firstRev+8, firstRev+8, 1)
+
+ inputs := []*mvccpb.KeyValue{kvA, kvB, kvCV1, kvCV2, kvC, kvFoo, kvFooAbc, kvFop}
+ for i := range inputs {
+ _, putError := cc.Put(ctx, string(inputs[i].Key), string(inputs[i].Value), config.PutOptions{})
+ require.NoErrorf(t, putError, "count not put key value %q", inputs[i])
}
- tests := []struct {
+
+ allKvs := []*mvccpb.KeyValue{kvA, kvB, kvC, kvFoo, kvFooAbc, kvFop}
+ kvsByVersion := []*mvccpb.KeyValue{kvA, kvB, kvFoo, kvFooAbc, kvFop, kvC}
+ reversedKvs := []*mvccpb.KeyValue{kvFop, kvFooAbc, kvFoo, kvC, kvB, kvA}
+ kvsByValue := []*mvccpb.KeyValue{kvFooAbc, kvB, kvA, kvC, kvFoo, kvFop}
+ kvsByValueDesc := []*mvccpb.KeyValue{kvFop, kvFoo, kvC, kvA, kvB, kvFooAbc}
+
+ currentResp, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
+ require.NoError(t, err)
+ currentHeader := &etcdserverpb.ResponseHeader{
+ ClusterId: currentResp.Header.ClusterId,
+ Revision: currentResp.Header.Revision,
+ }
+
+ type testcase struct {
+ name string
begin string
- end string
options config.GetOptions
- wkv []string
- }{
- {begin: "a", wkv: wantKvs[:1]},
- {begin: "a", options: config.GetOptions{Serializable: true}, wkv: wantKvs[:1]},
- {begin: "a", options: config.GetOptions{End: "c"}, wkv: wantKvs[:2]},
- {begin: "", options: config.GetOptions{Prefix: true}, wkv: wantKvs},
- {begin: "", options: config.GetOptions{FromKey: true}, wkv: wantKvs},
- {begin: "a", options: config.GetOptions{End: "x"}, wkv: wantKvs},
- {begin: "", options: config.GetOptions{Prefix: true, Revision: 4}, wkv: kvs[:3]},
- {begin: "a", options: config.GetOptions{CountOnly: true}, wkv: nil},
- {begin: "foo", options: config.GetOptions{Prefix: true}, wkv: []string{"foo", "foo/abc"}},
- {begin: "foo", options: config.GetOptions{FromKey: true}, wkv: []string{"foo", "foo/abc", "fop"}},
- {begin: "", options: config.GetOptions{Prefix: true, Limit: 2}, wkv: wantKvs[:2]},
- {begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByModRevision}, wkv: wantKvs},
- {begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByVersion}, wkv: kvsByVersion},
- {begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortNone, SortBy: clientv3.SortByCreateRevision}, wkv: wantKvs},
- {begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend, SortBy: clientv3.SortByCreateRevision}, wkv: reversedKvs},
- {begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend, SortBy: clientv3.SortByKey}, wkv: reversedKvs},
+ wantResponse *clientv3.GetResponse
}
- for _, tt := range tests {
- resp, err := cc.Get(ctx, tt.begin, tt.options)
- require.NoErrorf(t, err, "count not get key %q, err: %s", tt.begin, err)
- kvs := testutils.KeysFromGetResponse(resp)
- assert.Equal(t, tt.wkv, kvs)
+ tests := []testcase{
+ {name: "Get one specific key (a)", begin: "a", wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvA}}},
+ {name: "Get one specific key (a), serializable", begin: "a", options: config.GetOptions{Serializable: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvA}}},
+ {name: "Get [a, c)", begin: "a", options: config.GetOptions{End: "c"}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 2, Kvs: allKvs[:2]}},
+ {name: "blank key with --prefix option -> all KVs", begin: "", options: config.GetOptions{Prefix: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs}},
+ {name: "blank key with --from-key option -> all KVs", begin: "", options: config.GetOptions{FromKey: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs}},
+ {name: "Range covering all keys -> all KVs", begin: "a", options: config.GetOptions{End: "x"}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs}},
+ {name: "blank key with --prefix and revision -> [first key, entry at specified revision]", begin: "", options: config.GetOptions{Prefix: true, Revision: int(firstRev + 3)}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 3, Kvs: []*mvccpb.KeyValue{kvA, kvB, kvCV1}}},
+ {name: "--count-only for one single key", begin: "a", options: config.GetOptions{CountOnly: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: nil}},
+ {name: "--prefix of foo -> all entries with the prefix", begin: "foo", options: config.GetOptions{Prefix: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 2, Kvs: allKvs[3:5]}},
+ {name: "--from-key of 'foo' -> ", begin: "foo", options: config.GetOptions{FromKey: true}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 3, Kvs: allKvs[3:]}},
+ {name: "blank key with limit set", begin: "", options: config.GetOptions{Prefix: true, Limit: 2}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs[:2], More: true}},
+ {name: "all kvs ordered by mod revision ascending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByModRevision}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs}},
+ {name: "all KVs ordered by version ascending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByVersion}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: kvsByVersion}},
+ {name: "all KVs ordered by key ascending, limit 2", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByKey, Limit: 2}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: []*mvccpb.KeyValue{kvA, kvB}, More: true}},
+ {name: "range [b, z) ordered by key descending, limit 2", begin: "b", options: config.GetOptions{End: "z", Order: clientv3.SortDescend, SortBy: clientv3.SortByKey, Limit: 2}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 5, Kvs: []*mvccpb.KeyValue{kvFop, kvFooAbc}, More: true}},
+ {name: "all KVs ordered by create revision, unspecified sort order", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortNone, SortBy: clientv3.SortByCreateRevision}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs}},
+ {name: "all KVs ordered by create revision descending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend, SortBy: clientv3.SortByCreateRevision}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: reversedKvs}},
+ {name: "all KVs ordered by key descending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend, SortBy: clientv3.SortByKey}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: reversedKvs}},
+ {name: "all KVs ordered by value, unspecified sort order", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortNone, SortBy: clientv3.SortByValue}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: kvsByValue}},
+ {name: "all KVs ordered by value, ascending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortAscend, SortBy: clientv3.SortByValue}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: kvsByValue}},
+ {name: "all KVs ordered by value descending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend, SortBy: clientv3.SortByValue}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: kvsByValueDesc}},
+ {name: "all KVs descending", begin: "", options: config.GetOptions{Prefix: true, Order: clientv3.SortDescend}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: reversedKvs}},
+ {name: "Get first version of 'c' by its revision", begin: "c", options: config.GetOptions{Revision: int(firstRev) + 3}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvCV1}}},
+ {name: "Get second version of 'c' by its revision", begin: "c", options: config.GetOptions{Revision: int(firstRev) + 4}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvCV2}}},
+ {name: "Get third version of 'c' by its revision", begin: "c", options: config.GetOptions{Revision: int(firstRev) + 5}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvC}}},
+ {name: "Get the latest version of 'c'", begin: "c", wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 1, Kvs: []*mvccpb.KeyValue{kvC}}},
+ {name: "all KVs with mininum mod revision sorted by mod revision", begin: "", options: config.GetOptions{Prefix: true, MinModRevision: int(firstRev) + 3, SortBy: clientv3.SortByModRevision}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs[2:]}},
+ {name: "all KVs with maximum mod revision, sorted by key descending", begin: "", options: config.GetOptions{Prefix: true, MaxModRevision: int(firstRev) + 4, Order: clientv3.SortDescend, SortBy: clientv3.SortByKey}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: reversedKvs[4:]}},
+ {name: "all KVs with minimum create revision, sorted by version, descending", begin: "", options: config.GetOptions{Prefix: true, MinCreateRevision: int(firstRev) + 3, Order: clientv3.SortDescend, SortBy: clientv3.SortByVersion}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: allKvs[2:]}},
+ {name: "all KVs with maximimum create revision, sorted by value", begin: "", options: config.GetOptions{Prefix: true, MaxCreateRevision: int(firstRev) + 6, Order: clientv3.SortDescend, SortBy: clientv3.SortByValue}, wantResponse: &clientv3.GetResponse{Header: currentHeader, Count: 6, Kvs: kvsByValueDesc[1:5]}},
+ }
+ testsWithKeysOnly := make([]testcase, 0, len(tests))
+ for _, otc := range tests {
+ if otc.options.CountOnly {
+ continue // can't use both --count-only and --keys-only at the same time
+ }
+ withKeysOnly := otc
+ withKeysOnly.name = fmt.Sprintf("%s --keys-only", withKeysOnly.name)
+ withKeysOnly.options.KeysOnly = true
+ withKeysOnly.wantResponse = cloneGetResponseWithoutValues(otc.wantResponse)
+ testsWithKeysOnly = append(testsWithKeysOnly, withKeysOnly)
+ }
+ for _, tt := range slices.Concat(tests, testsWithKeysOnly) {
+ t.Run(tt.name, func(t *testing.T) {
+ if stream && !rangeStreamSupports(tt.options) {
+ t.Skip("options not supported by RangeStream")
+ }
+ opts := tt.options
+ opts.Stream = stream
+ resp, err := cc.Get(ctx, tt.begin, opts)
+ require.NoErrorf(t, err, "count not get key %q, err: %s", tt.begin, err)
+ resp.Header.MemberId = 0
+ resp.Header.RaftTerm = 0
+ assert.Emptyf(t,
+ cmp.Diff(
+ (*etcdserverpb.RangeResponse)(tt.wantResponse),
+ (*etcdserverpb.RangeResponse)(resp),
+ protocmp.Transform(),
+ ),
+ "-want, +got")
+ })
}
})
})
}
}
+func createKV(key, val string, createRev, modRev, ver int64) *mvccpb.KeyValue {
+ return &mvccpb.KeyValue{
+ Key: []byte(key),
+ Value: []byte(val),
+ CreateRevision: createRev,
+ ModRevision: modRev,
+ Version: ver,
+ }
+}
+
+// clusterSupportsGetStream probes every cluster member with a RangeStream RPC and returns false if any member rejects it.
+func clusterSupportsGetStream(ctx context.Context, t *testing.T, clus interfaces.Cluster) bool {
+ for _, m := range clus.Members() {
+ _, err := m.Client().Get(ctx, "probe", config.GetOptions{Stream: true})
+ if err != nil {
+ t.Logf("member does not support RangeStream: %v", err)
+ return false
+ }
+ }
+ return true
+}
+
+// rangeStreamSupports reports whether the server's RangeStream RPC accepts a
+// request with these options, mirroring v3rpc.checkRangeStreamRequest.
+func rangeStreamSupports(o config.GetOptions) bool {
+ if !txn.IsDefaultOrdering(
+ etcdserverpb.RangeRequest_SortTarget(o.SortBy),
+ etcdserverpb.RangeRequest_SortOrder(o.Order),
+ ) {
+ return false
+ }
+ return !txn.HasRevisionFilters(&etcdserverpb.RangeRequest{
+ MinModRevision: int64(o.MinModRevision),
+ MaxModRevision: int64(o.MaxModRevision),
+ MinCreateRevision: int64(o.MinCreateRevision),
+ MaxCreateRevision: int64(o.MaxCreateRevision),
+ })
+}
+
+func cloneGetResponseWithoutValues(resp *clientv3.GetResponse) *clientv3.GetResponse {
+ clone := cloneGetResponse(resp)
+ if clone == nil {
+ return nil
+ }
+ for _, kv := range clone.Kvs {
+ if kv != nil {
+ kv.Value = nil
+ }
+ }
+ return clone
+}
+
+func cloneGetResponse(resp *clientv3.GetResponse) *clientv3.GetResponse {
+ if resp == nil {
+ return nil
+ }
+ return (*clientv3.GetResponse)(
+ proto.Clone(
+ (*etcdserverpb.RangeResponse)(resp),
+ ).(*etcdserverpb.RangeResponse),
+ )
+}
+
func TestKVDelete(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -166,7 +313,8 @@ func TestKVDelete(t *testing.T) {
}
for _, tt := range tests {
for i := range kvs {
- require.NoErrorf(t, cc.Put(ctx, kvs[i], "bar", config.PutOptions{}), "count not put key %q", kvs[i])
+ _, err := cc.Put(ctx, kvs[i], "bar", config.PutOptions{})
+ require.NoErrorf(t, err, "count not put key %q", kvs[i])
}
del, err := cc.Delete(ctx, tt.deleteKey, tt.options)
require.NoErrorf(t, err, "count not get key %q, err", tt.deleteKey)
@@ -201,7 +349,7 @@ func TestKVGetNoQuorum(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
defer clus.Close()
diff --git a/tests/common/lease_test.go b/tests/common/lease_test.go
index 6322559c185a..d94eafe9b252 100644
--- a/tests/common/lease_test.go
+++ b/tests/common/lease_test.go
@@ -31,7 +31,7 @@ func TestLeaseGrantTimeToLive(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -74,7 +74,7 @@ func TestLeaseGrantAndList(t *testing.T) {
for _, nc := range nestedCases {
t.Run(tc.name+"/"+nc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
t.Logf("Creating cluster...")
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
@@ -122,7 +122,7 @@ func TestLeaseGrantTimeToLiveExpired(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -132,18 +132,18 @@ func TestLeaseGrantTimeToLiveExpired(t *testing.T) {
leaseResp, err := cc.Grant(ctx, 2)
require.NoError(t, err)
- err = cc.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
+ _, err = cc.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
require.NoError(t, err)
getResp, err := cc.Get(ctx, "foo", config.GetOptions{})
require.NoError(t, err)
require.Equal(t, int64(1), getResp.Count)
- // When leader changes, old leader steps back to follower
- // and may ignore the lease revoking. The new leader will
- // restart TTL counting. If so, we should call time.Sleep
- // again and wait for revoking. It can't completely avoid
- // flaky but reduce flaky possibility.
+ // FIXME: When leader changes, old leader steps
+ // back to follower and ignores the lease revoking.
+ // The new leader will restart TTL counting. If so,
+ // we should call time.Sleep again and wait for revoking.
+ // It can't avoid flakey but reduce flakey possibility.
for i := 0; i < 3; i++ {
currentLeader := clus.WaitLeader(t)
t.Logf("[%d] current leader index %d", i, currentLeader)
@@ -175,7 +175,7 @@ func TestLeaseGrantKeepAliveOnce(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -192,7 +192,7 @@ func TestLeaseGrantKeepAliveOnce(t *testing.T) {
// back to follower and ignores the lease revoking.
// The new leader will restart TTL counting. If so,
// we should call time.Sleep again and wait for revoking.
- // It can't avoid flakey but reduce flakey possiblility.
+ // It can't avoid flakey but reduce flakey possibility.
for i := 0; i < 3; i++ {
currentLeader := clus.WaitLeader(t)
t.Logf("[%d] current leader index %d", i, currentLeader)
@@ -220,7 +220,7 @@ func TestLeaseGrantRevoke(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -230,7 +230,7 @@ func TestLeaseGrantRevoke(t *testing.T) {
leaseResp, err := cc.Grant(ctx, 20)
require.NoError(t, err)
- err = cc.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
+ _, err = cc.Put(ctx, "foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
require.NoError(t, err)
getResp, err := cc.Get(ctx, "foo", config.GetOptions{})
diff --git a/tests/common/maintenance_auth_test.go b/tests/common/maintenance_auth_test.go
index 21a72580ede4..87612c82e302 100644
--- a/tests/common/maintenance_auth_test.go
+++ b/tests/common/maintenance_auth_test.go
@@ -166,7 +166,7 @@ func TestStatusWithRootAuth(t *testing.T) {
}
func TestStatusWithUserAuth(t *testing.T) {
- testStatusWithAuth(t, false, true, WithAuth("user0", "user0Pass"))
+ testStatusWithAuth(t, false, false, WithAuth("user0", "user0Pass"))
}
func testStatusWithAuth(t *testing.T, expectConnectionError, expectOperationError bool, opts ...config.ClientOption) {
@@ -203,7 +203,7 @@ func setupAuthForMaintenanceTest(c intf.Client) error {
func testMaintenanceOperationWithAuth(t *testing.T, expectConnectError, expectOperationError bool, f func(context.Context, intf.Client) error, opts ...config.ClientOption) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t)
diff --git a/tests/common/member_test.go b/tests/common/member_test.go
index a3e0251403e7..10037a42e157 100644
--- a/tests/common/member_test.go
+++ b/tests/common/member_test.go
@@ -34,7 +34,7 @@ func TestMemberList(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -114,7 +114,7 @@ func TestMemberAdd(t *testing.T) {
if quorumTc.waitForQuorum {
ctxTimeout += etcdserver.HealthInterval
}
- ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), ctxTimeout)
defer cancel()
c := clusterTc.config
c.StrictReconfigCheck = quorumTc.strictReconfigCheck
@@ -189,7 +189,7 @@ func TestMemberRemove(t *testing.T) {
continue
}
t.Run(quorumTc.name+"/"+clusterTc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 14*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 14*time.Second)
defer cancel()
c := clusterTc.config
c.StrictReconfigCheck = quorumTc.strictReconfigCheck
diff --git a/tests/common/role_test.go b/tests/common/role_test.go
index 7dda25d303b2..2eb1af1e6b87 100644
--- a/tests/common/role_test.go
+++ b/tests/common/role_test.go
@@ -31,7 +31,7 @@ func TestRoleAdd_Simple(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -47,7 +47,7 @@ func TestRoleAdd_Simple(t *testing.T) {
func TestRoleAdd_Error(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterSize(1))
defer clus.Close()
@@ -64,7 +64,7 @@ func TestRoleAdd_Error(t *testing.T) {
func TestRootRole(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterSize(1))
defer clus.Close()
@@ -86,7 +86,7 @@ func TestRootRole(t *testing.T) {
func TestRoleGrantRevokePermission(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterSize(1))
defer clus.Close()
@@ -109,7 +109,7 @@ func TestRoleGrantRevokePermission(t *testing.T) {
func TestRoleDelete(t *testing.T) {
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterSize(1))
defer clus.Close()
diff --git a/tests/common/status_test.go b/tests/common/status_test.go
index 519bb07e4355..ec6e1c219ca2 100644
--- a/tests/common/status_test.go
+++ b/tests/common/status_test.go
@@ -30,7 +30,7 @@ func TestStatus(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
diff --git a/tests/common/txn_test.go b/tests/common/txn_test.go
index b9c1b688c2f5..b3b0df601615 100644
--- a/tests/common/txn_test.go
+++ b/tests/common/txn_test.go
@@ -59,15 +59,15 @@ func TestTxnSucc(t *testing.T) {
}
for _, cfg := range clusterTestCases() {
t.Run(cfg.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(cfg.config))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- err := cc.Put(ctx, "key1", "value1", config.PutOptions{})
+ _, err := cc.Put(ctx, "key1", "value1", config.PutOptions{})
require.NoErrorf(t, err, "could not create key:%s, value:%s", "key1", "value1")
- err = cc.Put(ctx, "key2", "value2", config.PutOptions{})
+ _, err = cc.Put(ctx, "key2", "value2", config.PutOptions{})
require.NoErrorf(t, err, "could not create key:%s, value:%s", "key2", "value2")
for _, req := range reqs {
resp, err := cc.Txn(ctx, req.compare, req.ifSuccess, req.ifFail, config.TxnOptions{
@@ -99,13 +99,13 @@ func TestTxnFail(t *testing.T) {
}
for _, cfg := range clusterTestCases() {
t.Run(cfg.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(cfg.config))
defer clus.Close()
cc := testutils.MustClient(clus.Client())
testutils.ExecuteUntil(ctx, t, func() {
- err := cc.Put(ctx, "key1", "value1", config.PutOptions{})
+ _, err := cc.Put(ctx, "key1", "value1", config.PutOptions{})
require.NoErrorf(t, err, "could not create key:%s, value:%s", "key1", "value1")
for _, req := range reqs {
resp, err := cc.Txn(ctx, req.compare, req.ifSuccess, req.ifFail, config.TxnOptions{
@@ -129,17 +129,17 @@ func getRespValues(r *clientv3.TxnResponse) []string {
for _, resp := range r.Responses {
switch v := resp.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
- r := (clientv3.DeleteResponse)(*v.ResponseDeleteRange)
- ss = append(ss, fmt.Sprintf("%d", r.Deleted))
+ r := v.ResponseDeleteRange
+ ss = append(ss, fmt.Sprintf("%d", r.GetDeleted()))
case *pb.ResponseOp_ResponsePut:
- r := (clientv3.PutResponse)(*v.ResponsePut)
+ r := v.ResponsePut
ss = append(ss, "OK")
- if r.PrevKv != nil {
+ if r.GetPrevKv() != nil {
ss = append(ss, string(r.PrevKv.Key), string(r.PrevKv.Value))
}
case *pb.ResponseOp_ResponseRange:
- r := (clientv3.GetResponse)(*v.ResponseRange)
- for _, kv := range r.Kvs {
+ r := v.ResponseRange
+ for _, kv := range r.GetKvs() {
ss = append(ss, string(kv.Key), string(kv.Value))
}
default:
diff --git a/tests/common/unit_test.go b/tests/common/unit_test.go
index 4b172e7a3cb4..52fe5d16b3ba 100644
--- a/tests/common/unit_test.go
+++ b/tests/common/unit_test.go
@@ -37,6 +37,22 @@ func WithAuth(userName, password string) config.ClientOption {
return func(any) {}
}
+func WithAuthToken(token string) config.ClientOption {
+ return func(any) {}
+}
+
func WithEndpoints(endpoints []string) config.ClientOption {
return func(any) {}
}
+
+func WithHTTP2Debug() config.ClusterOption {
+ return func(c *config.ClusterConfig) {}
+}
+
+func WithTCPClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {}
+}
+
+func WithUnixClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {}
+}
diff --git a/tests/common/user_test.go b/tests/common/user_test.go
index 7f189b000b7e..dc8e64871a94 100644
--- a/tests/common/user_test.go
+++ b/tests/common/user_test.go
@@ -66,7 +66,7 @@ func TestUserAdd_Simple(t *testing.T) {
for _, tc := range clusterTestCases() {
for _, nc := range tcs {
t.Run(tc.name+"/"+nc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -90,7 +90,7 @@ func TestUserAdd_DuplicateUserNotAllowed(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -114,7 +114,7 @@ func TestUserList(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -145,7 +145,7 @@ func TestUserDelete(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -182,7 +182,7 @@ func TestUserChangePassword(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
diff --git a/tests/common/wait_leader_test.go b/tests/common/wait_leader_test.go
index 5f9a8ba0572c..fd15a8ae5773 100644
--- a/tests/common/wait_leader_test.go
+++ b/tests/common/wait_leader_test.go
@@ -29,7 +29,7 @@ func TestWaitLeader(t *testing.T) {
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
@@ -56,7 +56,7 @@ func TestWaitLeader_MemberStop(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
defer clus.Close()
diff --git a/tests/common/watch_test.go b/tests/common/watch_test.go
index fe6d6b0c8f25..0ab3a4d5ab0b 100644
--- a/tests/common/watch_test.go
+++ b/tests/common/watch_test.go
@@ -31,7 +31,7 @@ func TestWatch(t *testing.T) {
watchTimeout := 1 * time.Second
for _, tc := range clusterTestCases() {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 20*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterConfig(tc.config))
@@ -76,7 +76,7 @@ func TestWatch(t *testing.T) {
require.NotNilf(t, wch, "failed to watch %s", tt.watchKey)
for j := range tt.puts {
- err := cc.Put(ctx, tt.puts[j].Key, tt.puts[j].Val, config.PutOptions{})
+ _, err := cc.Put(ctx, tt.puts[j].Key, tt.puts[j].Val, config.PutOptions{})
require.NoErrorf(t, err, "can't not put key %q, err: %s", tt.puts[j].Key, err)
}
diff --git a/tests/e2e/cluster_downgrade_test.go b/tests/e2e/cluster_downgrade_test.go
index 0d80f0c4b82d..9b8bce77b5b6 100644
--- a/tests/e2e/cluster_downgrade_test.go
+++ b/tests/e2e/cluster_downgrade_test.go
@@ -137,7 +137,7 @@ func testDowngradeUpgrade(t *testing.T, numberOfMembersToDowngrade int, clusterS
e2e.ValidateDowngradeInfo(t, epc, &pb.DowngradeInfo{Enabled: false})
t.Log("Adding member to test membership, but a learner avoid breaking quorum")
- resp, err := cc.MemberAddAsLearner(context.Background(), "fake1", []string{"http://127.0.0.1:1001"})
+ resp, err := cc.MemberAddAsLearner(t.Context(), "fake1", []string{"http://127.0.0.1:1001"})
require.NoError(t, err)
if triggerSnapshot {
t.Logf("Generating snapshot")
@@ -145,7 +145,7 @@ func testDowngradeUpgrade(t *testing.T, numberOfMembersToDowngrade int, clusterS
verifySnapshot(t, epc)
}
t.Log("Removing learner to test membership")
- _, err = cc.MemberRemove(context.Background(), resp.Member.ID)
+ _, err = cc.MemberRemove(t.Context(), resp.Member.ID)
require.NoError(t, err)
beforeMembers, beforeKV := getMembersAndKeys(t, cc)
@@ -197,7 +197,7 @@ func testDowngradeUpgrade(t *testing.T, numberOfMembersToDowngrade int, clusterS
}
t.Log("Adding learner to test membership, but avoid breaking quorum")
- resp, err = cc.MemberAddAsLearner(context.Background(), "fake2", []string{"http://127.0.0.1:1002"})
+ resp, err = cc.MemberAddAsLearner(t.Context(), "fake2", []string{"http://127.0.0.1:1002"})
require.NoError(t, err)
if triggerSnapshot {
t.Logf("Generating snapshot")
@@ -205,7 +205,7 @@ func testDowngradeUpgrade(t *testing.T, numberOfMembersToDowngrade int, clusterS
verifySnapshot(t, epc)
}
t.Log("Removing learner to test membership")
- _, err = cc.MemberRemove(context.Background(), resp.Member.ID)
+ _, err = cc.MemberRemove(t.Context(), resp.Member.ID)
require.NoError(t, err)
beforeMembers, beforeKV = getMembersAndKeys(t, cc)
@@ -229,7 +229,7 @@ func testDowngradeUpgrade(t *testing.T, numberOfMembersToDowngrade int, clusterS
}
func newCluster(t *testing.T, clusterSize int, snapshotCount uint64) *e2e.EtcdProcessCluster {
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(clusterSize),
e2e.WithSnapshotCount(snapshotCount),
e2e.WithKeepDataDir(true),
@@ -246,13 +246,13 @@ func newCluster(t *testing.T, clusterSize int, snapshotCount uint64) *e2e.EtcdPr
}
func generateSnapshot(t *testing.T, snapshotCount uint64, cc *e2e.EtcdctlV3) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
var i uint64
t.Logf("Adding keys")
for i = 0; i < snapshotCount*3; i++ {
- err := cc.Put(ctx, fmt.Sprintf("%d", i), "1", config.PutOptions{})
+ _, err := cc.Put(ctx, fmt.Sprintf("%d", i), "1", config.PutOptions{})
assert.NoError(t, err)
}
}
@@ -286,7 +286,7 @@ func verifySnapshotMembers(t *testing.T, epc *e2e.EtcdProcessCluster, expectedMe
}
func getMembersAndKeys(t *testing.T, cc *e2e.EtcdctlV3) (*clientv3.MemberListResponse, *clientv3.GetResponse) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
kvs, err := cc.Get(ctx, "", config.GetOptions{Prefix: true})
diff --git a/tests/e2e/cmux_test.go b/tests/e2e/cmux_test.go
index 9281705ee049..20c6ed3c489e 100644
--- a/tests/e2e/cmux_test.go
+++ b/tests/e2e/cmux_test.go
@@ -27,6 +27,7 @@ import (
"testing"
"github.com/prometheus/common/expfmt"
+ "github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -69,7 +70,7 @@ func TestConnectionMultiplexing(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
cfg := e2e.NewConfig(e2e.WithClusterSize(1))
cfg.Client.ConnectionType = tc.serverTLS
cfg.ClientHTTPSeparate = tc.separateHTTPPort
@@ -189,7 +190,7 @@ func fetchMetrics(t *testing.T, endpoint string, httpVersion string, connType e2
}
respData := string(rawData)
- var parser expfmt.TextParser
+ parser := expfmt.NewTextParser(model.LegacyValidation)
_, err = parser.TextToMetricFamilies(strings.NewReader(strings.ReplaceAll(respData, "\r\n", "\n")))
return err
}
diff --git a/tests/e2e/corrupt_test.go b/tests/e2e/corrupt_test.go
index 86535345e997..6a1b1494bbf5 100644
--- a/tests/e2e/corrupt_test.go
+++ b/tests/e2e/corrupt_test.go
@@ -99,7 +99,7 @@ func corruptTest(cx ctlCtx) {
func TestInPlaceRecovery(t *testing.T) {
basePort := 20000
e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
// Initialize the cluster.
@@ -124,7 +124,7 @@ func TestInPlaceRecovery(t *testing.T) {
oldCc, err := e2e.NewEtcdctl(epcOld.Cfg.Client, epcOld.EndpointsGRPC())
require.NoError(t, err)
for i := 0; i < 10; i++ {
- err = oldCc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
+ _, err = oldCc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
require.NoErrorf(t, err, "error on put")
}
@@ -188,24 +188,13 @@ func TestInPlaceRecovery(t *testing.T) {
}
func TestPeriodicCheckDetectsCorruption(t *testing.T) {
- testPeriodicCheckDetectsCorruption(t, false)
-}
-
-func TestPeriodicCheckDetectsCorruptionWithExperimentalFlag(t *testing.T) {
- testPeriodicCheckDetectsCorruption(t, true)
-}
-
-func testPeriodicCheckDetectsCorruption(t *testing.T, useExperimentalFlag bool) {
checkTime := time.Second
e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
- var corruptCheckTime e2e.EPClusterOption
- if useExperimentalFlag {
- corruptCheckTime = e2e.WithExperimentalCorruptCheckTime(time.Second)
- } else {
- corruptCheckTime = e2e.WithCorruptCheckTime(time.Second)
- }
+
+ corruptCheckTime := e2e.WithCorruptCheckTime(time.Second)
+
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
e2e.WithKeepDataDir(true),
corruptCheckTime,
@@ -221,7 +210,7 @@ func testPeriodicCheckDetectsCorruption(t *testing.T, useExperimentalFlag bool)
cc := epc.Etcdctl()
for i := 0; i < 10; i++ {
- err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
+ _, err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
require.NoErrorf(t, err, "error on put")
}
@@ -233,7 +222,7 @@ func testPeriodicCheckDetectsCorruption(t *testing.T, useExperimentalFlag bool)
err = testutil.CorruptBBolt(datadir.ToBackendFileName(epc.Procs[0].Config().DataDirPath))
require.NoError(t, err)
- err = epc.Procs[0].Restart(context.TODO())
+ err = epc.Procs[0].Restart(t.Context())
require.NoError(t, err)
time.Sleep(checkTime * 11 / 10)
alarmResponse, err := cc.AlarmList(ctx)
@@ -252,7 +241,7 @@ func TestCompactHashCheckDetectCorruptionWithFeatureGate(t *testing.T) {
func testCompactHashCheckDetectCorruption(t *testing.T, useFeatureGate bool) {
checkTime := time.Second
e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
opts := []e2e.EPClusterOption{e2e.WithKeepDataDir(true), e2e.WithCompactHashCheckTime(checkTime)}
if useFeatureGate {
@@ -272,7 +261,7 @@ func testCompactHashCheckDetectCorruption(t *testing.T, useFeatureGate bool) {
cc := epc.Etcdctl()
for i := 0; i < 10; i++ {
- err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
+ _, err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i), config.PutOptions{})
require.NoErrorf(t, err, "error on put")
}
memberID, found, err := getMemberIDByName(ctx, cc, epc.Procs[0].Config().Name)
@@ -294,21 +283,17 @@ func testCompactHashCheckDetectCorruption(t *testing.T, useFeatureGate bool) {
}
func TestCompactHashCheckDetectCorruptionInterrupt(t *testing.T) {
- testCompactHashCheckDetectCorruptionInterrupt(t, false, false)
+ testCompactHashCheckDetectCorruptionInterrupt(t, false)
}
func TestCompactHashCheckDetectCorruptionInterruptWithFeatureGate(t *testing.T) {
- testCompactHashCheckDetectCorruptionInterrupt(t, true, false)
-}
-
-func TestCompactHashCheckDetectCorruptionInterruptWithExperimentalFlag(t *testing.T) {
- testCompactHashCheckDetectCorruptionInterrupt(t, true, true)
+ testCompactHashCheckDetectCorruptionInterrupt(t, true)
}
-func testCompactHashCheckDetectCorruptionInterrupt(t *testing.T, useFeatureGate bool, useExperimentalFlag bool) {
+func testCompactHashCheckDetectCorruptionInterrupt(t *testing.T, useFeatureGate bool) {
checkTime := time.Second
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 60*time.Second)
defer cancel()
slowCompactionNodeIndex := 1
@@ -329,12 +314,8 @@ func testCompactHashCheckDetectCorruptionInterrupt(t *testing.T, useFeatureGate
} else {
opts = append(opts, e2e.WithCompactHashCheckEnabled(true))
}
- var compactionBatchLimit e2e.EPClusterOption
- if useExperimentalFlag {
- compactionBatchLimit = e2e.WithExperimentalCompactionBatchLimit(1)
- } else {
- compactionBatchLimit = e2e.WithCompactionBatchLimit(1)
- }
+
+ compactionBatchLimit := e2e.WithCompactionBatchLimit(1)
cfg := e2e.NewConfig(opts...)
epc, err := e2e.InitEtcdProcessCluster(t, cfg)
@@ -360,7 +341,7 @@ func testCompactHashCheckDetectCorruptionInterrupt(t *testing.T, useFeatureGate
t.Log("putting 10 values to the identical key...")
cc := epc.Etcdctl()
for i := 0; i < 10; i++ {
- err = cc.Put(ctx, "key", fmt.Sprint(i), config.PutOptions{})
+ _, err = cc.Put(ctx, "key", fmt.Sprint(i), config.PutOptions{})
require.NoErrorf(t, err, "error on put")
}
@@ -407,7 +388,7 @@ func TestCtlV3LinearizableRead(t *testing.T) {
func testCtlV3ReadAfterWrite(t *testing.T, ops ...clientv3.OpOption) {
e2e.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
e2e.WithClusterSize(1),
diff --git a/tests/e2e/ctl_v3_auth_cluster_test.go b/tests/e2e/ctl_v3_auth_cluster_test.go
index 35b7cd289bed..37dd833b599c 100644
--- a/tests/e2e/ctl_v3_auth_cluster_test.go
+++ b/tests/e2e/ctl_v3_auth_cluster_test.go
@@ -30,7 +30,7 @@ import (
func TestAuthCluster(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
@@ -58,7 +58,7 @@ func TestAuthCluster(t *testing.T) {
// write more than SnapshotCount keys to single leader to make sure snapshot is created
for i := 0; i <= 10; i++ {
- if err := epc.Etcdctl(testUserClientOpts).Put(ctx, fmt.Sprintf("/test/%d", i), "test", config.PutOptions{}); err != nil {
+ if _, err := epc.Etcdctl(testUserClientOpts).Put(ctx, fmt.Sprintf("/test/%d", i), "test", config.PutOptions{}); err != nil {
t.Fatalf("failed to Put (%v)", err)
}
}
@@ -72,7 +72,7 @@ func TestAuthCluster(t *testing.T) {
endpoints := epc.EndpointsGRPC()
assert.Len(t, endpoints, 2)
for _, endpoint := range epc.EndpointsGRPC() {
- if err := epc.Etcdctl(testUserClientOpts, e2e.WithEndpoints([]string{endpoint})).Put(ctx, "/test/key", endpoint, config.PutOptions{}); err != nil {
+ if _, err := epc.Etcdctl(testUserClientOpts, e2e.WithEndpoints([]string{endpoint})).Put(ctx, "/test/key", endpoint, config.PutOptions{}); err != nil {
t.Fatalf("failed to write to Put to %q (%v)", endpoint, err)
}
}
diff --git a/tests/e2e/ctl_v3_auth_no_proxy_test.go b/tests/e2e/ctl_v3_auth_no_proxy_test.go
index 8529ff38dc98..da00e93e21d2 100644
--- a/tests/e2e/ctl_v3_auth_no_proxy_test.go
+++ b/tests/e2e/ctl_v3_auth_no_proxy_test.go
@@ -43,7 +43,7 @@ func TestCtlV3AuthCertCNAndUsernameNoPassword(t *testing.T) {
func TestCtlV3AuthCertCNWithWithConcurrentOperation(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
// apply the certificate which has `root` CommonName,
@@ -113,7 +113,7 @@ func TestCtlV3AuthCertCNWithWithConcurrentOperation(t *testing.T) {
key := fmt.Sprintf("key-%d", i)
value := fmt.Sprintf("value-%d", i)
- if err := epcClient.Put(ctx, key, value, config.PutOptions{}); err != nil {
+ if _, err := epcClient.Put(ctx, key, value, config.PutOptions{}); err != nil {
errs <- fmt.Errorf("failed to put key %q: %w", key, err)
break
}
diff --git a/tests/e2e/ctl_v3_auth_test.go b/tests/e2e/ctl_v3_auth_test.go
index 32256d2e6030..8f06e27d9433 100644
--- a/tests/e2e/ctl_v3_auth_test.go
+++ b/tests/e2e/ctl_v3_auth_test.go
@@ -45,6 +45,19 @@ func TestCtlV3AuthSnapshotJWT(t *testing.T) {
testCtl(t, authTestSnapshot, withCfg(*e2e.NewConfigJWT()))
}
+func TestCtlV3GetAuthStatus(t *testing.T) { testCtl(t, authTestGetAuthStatus) }
+
+func ctlV3AuthStatus(cx ctlCtx, expected string) error {
+ cmd := append(cx.PrefixArgs(), "auth", "status")
+ return e2e.SpawnWithExpectWithEnv(cmd, cx.envMap, expect.ExpectedResponse{Value: expected})
+}
+
+func authTestGetAuthStatus(cx ctlCtx) {
+ require.NoError(cx.t, ctlV3AuthStatus(cx, "Authentication Status: false"))
+ require.NoError(cx.t, authEnable(cx))
+ require.NoError(cx.t, ctlV3AuthStatus(cx, "Authentication Status: true"))
+}
+
func authEnable(cx ctlCtx) error {
// create root user with root role
if err := ctlV3User(cx, []string{"add", "root", "--interactive=false"}, "User root created", []string{"root"}); err != nil {
diff --git a/tests/e2e/ctl_v3_kv_test.go b/tests/e2e/ctl_v3_kv_test.go
index 82211946c35b..b79ebe5515ee 100644
--- a/tests/e2e/ctl_v3_kv_test.go
+++ b/tests/e2e/ctl_v3_kv_test.go
@@ -55,6 +55,22 @@ func TestCtlV3GetRevokedCRL(t *testing.T) {
testCtl(t, testGetRevokedCRL, withCfg(*cfg))
}
+// TestCtlV3GetRevokedCRLGRPCOnly is the same as TestCtlV3GetRevokedCRL but uses
+// --listen-client-http-urls to split HTTP and gRPC onto separate ports. In that
+// mode the gRPC server owns the TLS handshake directly (onlyGRPC=true in
+// serve.go) and CRL checking must be injected via ConfigureCRLVerification
+// rather than through the wrapping TLS listener used in the combined path.
+func TestCtlV3GetRevokedCRLGRPCOnly(t *testing.T) {
+ cfg := e2e.NewConfig(
+ e2e.WithClusterSize(1),
+ e2e.WithClientConnType(e2e.ClientTLS),
+ e2e.WithClientRevokeCerts(true),
+ e2e.WithClientCertAuthority(true),
+ e2e.WithClientHTTPSeparate(true),
+ )
+ testCtl(t, testGetRevokedCRL, withCfg(*cfg))
+}
+
func testGetRevokedCRL(cx ctlCtx) {
// test reject
require.ErrorContains(cx.t, ctlV3Put(cx, "k", "v", ""), "context deadline exceeded")
diff --git a/tests/e2e/ctl_v3_member_no_proxy_test.go b/tests/e2e/ctl_v3_member_no_proxy_test.go
index be492f6a4c8c..983d4b356d9f 100644
--- a/tests/e2e/ctl_v3_member_no_proxy_test.go
+++ b/tests/e2e/ctl_v3_member_no_proxy_test.go
@@ -33,7 +33,7 @@ import (
func TestMemberReplace(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 20*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t)
@@ -78,7 +78,7 @@ func TestMemberReplace(t *testing.T) {
removedMemberPeerURL := member.Config().PeerURL.String()
_, err = cc.MemberAdd(ctx, memberName, []string{removedMemberPeerURL})
require.NoError(t, err)
- err = patchArgs(member.Config().Args, "initial-cluster-state", "existing")
+ err = e2e.PatchArgs(member.Config().Args, "initial-cluster-state", "existing")
require.NoError(t, err)
// Sleep 100ms to bypass the known issue https://github.com/etcd-io/etcd/issues/16687.
@@ -100,7 +100,7 @@ func TestMemberReplace(t *testing.T) {
func TestMemberReplaceWithLearner(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 20*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t)
@@ -146,7 +146,7 @@ func TestMemberReplaceWithLearner(t *testing.T) {
_, err = cc.MemberAddAsLearner(ctx, memberName, []string{removedMemberPeerURL})
require.NoError(t, err)
- err = patchArgs(member.Config().Args, "initial-cluster-state", "existing")
+ err = e2e.PatchArgs(member.Config().Args, "initial-cluster-state", "existing")
require.NoError(t, err)
// Sleep 100ms to bypass the known issue https://github.com/etcd-io/etcd/issues/16687.
diff --git a/tests/e2e/ctl_v3_member_test.go b/tests/e2e/ctl_v3_member_test.go
index 5c784a8651a9..234adbc28158 100644
--- a/tests/e2e/ctl_v3_member_test.go
+++ b/tests/e2e/ctl_v3_member_test.go
@@ -29,17 +29,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "go.etcd.io/bbolt"
"go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/client/pkg/v3/fileutil"
- "go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/pkg/v3/expect"
- "go.etcd.io/etcd/server/v3/etcdserver/api/membership"
- "go.etcd.io/etcd/server/v3/storage/datadir"
- "go.etcd.io/etcd/server/v3/storage/schema"
- "go.etcd.io/etcd/tests/v3/framework/config"
+ "go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/tests/v3/framework/e2e"
- "go.etcd.io/etcd/tests/v3/framework/testutils"
)
func TestCtlV3MemberList(t *testing.T) { testCtl(t, memberListTest) }
@@ -86,7 +79,7 @@ func TestCtlV3MemberUpdatePeerTLS(t *testing.T) {
func TestCtlV3ConsistentMemberList(t *testing.T) {
e2e.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
e2e.WithClusterSize(1),
@@ -149,7 +142,9 @@ func TestCtlV3ConsistentMemberList(t *testing.T) {
default:
}
- mresp, merr := epc.Etcdctl().MemberList(ctx, true)
+ // Defailt timeout is 2s. We need to set a longer
+ // timeout here to make sure we can get the response.
+ mresp, merr := epc.Etcdctl(e2e.WithDialTimeout(5*time.Second)).MemberList(ctx, true)
if merr != nil {
continue
}
@@ -193,7 +188,7 @@ func ctlV3MemberList(cx ctlCtx) error {
return e2e.SpawnWithExpects(cmdArgs, cx.envMap, lines...)
}
-func getMemberList(cx ctlCtx, serializable bool) (etcdserverpb.MemberListResponse, error) {
+func getMemberList(cx ctlCtx, serializable bool) (*etcdserverpb.MemberListResponse, error) {
cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "member", "list")
if serializable {
cmdArgs = append(cmdArgs, "--consistency", "s")
@@ -201,23 +196,23 @@ func getMemberList(cx ctlCtx, serializable bool) (etcdserverpb.MemberListRespons
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
if err != nil {
- return etcdserverpb.MemberListResponse{}, err
+ return nil, err
}
var txt string
txt, err = proc.Expect("members")
if err != nil {
- return etcdserverpb.MemberListResponse{}, err
+ return nil, err
}
if err = proc.Close(); err != nil {
- return etcdserverpb.MemberListResponse{}, err
+ return nil, err
}
resp := etcdserverpb.MemberListResponse{}
dec := json.NewDecoder(strings.NewReader(txt))
if err := dec.Decode(&resp); errors.Is(err, io.EOF) {
- return etcdserverpb.MemberListResponse{}, err
+ return nil, err
}
- return resp, nil
+ return &resp, nil
}
func memberListWithHexTest(cx ctlCtx) {
@@ -350,7 +345,7 @@ func ctlV3MemberUpdate(cx ctlCtx, memberID, peerURL string) error {
func TestRemoveNonExistingMember(t *testing.T) {
e2e.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
cfg := e2e.ConfigStandalone(*e2e.NewConfig())
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithConfig(cfg))
@@ -365,229 +360,78 @@ func TestRemoveNonExistingMember(t *testing.T) {
assert.NoError(t, epc.Restart(ctx))
}
-// TestClusterUpgradeWithPromotedLearner tests whether etcd can automatically
-// fix the issue caused by https://github.com/etcd-io/etcd/issues/19557
-// when upgrading from 3.5 to 3.6.
-func TestClusterUpgradeWithPromotedLearner(t *testing.T) {
+// TestCtlV3MemberAddAsLearnerWithOneMemberDown verifies the case
+// of adding new member when one or two existing members are down.
+// Refer to https://github.com/etcd-io/etcd/issues/21640
+func TestCtlV3MemberAddAsLearnerWithOneMemberDown(t *testing.T) {
testCases := []struct {
- name string
- snapshotCount uint64
- writeToV3StoreSuccess bool
+ name string
+ clusterSize int
+ downMembers int
+ expectErr bool
}{
{
- name: "create snapshot after learner promotion which is not saved to v3store",
- snapshotCount: 10,
+ name: "0 out of 1 member is down, allow adding member",
+ clusterSize: 1,
+ downMembers: 0,
+ expectErr: false,
},
{
- name: "no snapshot and learner promotion is not saved to v3store",
- snapshotCount: 0,
+ name: "1 out of 3 members is down, reject adding member",
+ clusterSize: 3,
+ downMembers: 1,
+ expectErr: true,
},
{
- name: "no snapshot and learner promotion is saved to v3store",
- snapshotCount: 0,
- writeToV3StoreSuccess: true,
+ name: "1 out of 4 members is down, allow adding member",
+ clusterSize: 4,
+ downMembers: 1,
+ expectErr: false,
+ },
+ {
+ name: "1 out of 5 members is down, allow adding member",
+ clusterSize: 5,
+ downMembers: 1,
+ expectErr: false,
+ },
+ {
+ name: "2 out of 5 members are down, reject adding member",
+ clusterSize: 5,
+ downMembers: 2,
+ expectErr: true,
},
- }
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
e2e.BeforeTest(t)
+ ctx := t.Context()
- ctx := context.Background()
-
- epc, promotedMembers := mustCreateNewClusterByPromotingMembers(t, e2e.LastVersion, 2, e2e.WithSnapshotCount(tc.snapshotCount))
+ t.Logf("Bootstrap a cluster with %d members", tc.clusterSize)
+ epc, err := e2e.NewEtcdProcessCluster(ctx, t,
+ e2e.WithClusterSize(tc.clusterSize),
+ )
+ require.NoError(t, err)
defer func() {
- require.NoError(t, epc.Close())
+ _ = epc.Close()
}()
- require.Len(t, promotedMembers, 1)
- t.Logf("Promoted member: %+v", promotedMembers[0])
-
- if tc.snapshotCount != 0 {
- t.Logf("Write %d keys to trigger a snapshot", tc.snapshotCount)
- for i := 0; i < int(tc.snapshotCount); i++ {
- err := epc.Etcdctl().Put(ctx, fmt.Sprintf("key_%d", i), fmt.Sprintf("value_%d", i), config.PutOptions{})
- require.NoError(t, err)
- }
- }
-
- if tc.writeToV3StoreSuccess {
- t.Log("Skip manually changing the already promoted member to a learner in v3store")
- } else {
- t.Logf("Stopping all members")
- require.NoError(t, epc.Stop())
-
- t.Log("Manually changing the already promoted member to a learner again in the v3store of all members to simulate the issue https://github.com/etcd-io/etcd/issues/19557")
- promotedMembers[0].IsLearner = true
- for i := 0; i < len(epc.Procs); i++ {
- mustSaveMemberIntoBbolt(t, epc.Procs[i].Config().DataDirPath, promotedMembers[0])
- }
- }
-
- t.Log("Upgrading the cluster")
- currentVersion, err := e2e.GetVersionFromBinary(e2e.BinPath.Etcd)
- require.NoError(t, err)
- lastVersion, err := e2e.GetVersionFromBinary(e2e.BinPath.EtcdLastRelease)
- require.NoError(t, err)
- err = e2e.DowngradeUpgradeMembers(t, nil, epc, len(epc.Procs), false, lastVersion, currentVersion)
- require.NoError(t, err)
-
- t.Log("Check the expected log message")
- for _, proc := range epc.Procs {
- if tc.snapshotCount != 0 {
- e2e.AssertProcessLogs(t, proc, "Syncing member in v3store")
- } else {
- if tc.writeToV3StoreSuccess {
- e2e.AssertProcessLogs(t, proc, "ignore already promoted member")
- } else {
- e2e.AssertProcessLogs(t, proc, "Forcibly apply member promotion request")
- }
- }
- }
-
- t.Log("Checking all members are ready to serve client requests")
- for i := 0; i < len(epc.Procs); i++ {
- e2e.AssertProcessLogs(t, epc.Procs[i], e2e.EtcdServerReadyLines[0])
- }
-
- t.Log("Ensure all members in v3store are voting members again")
- for i := 0; i < len(epc.Procs); i++ {
- t.Logf("Stopping the member: %d", i)
- require.NoError(t, epc.Procs[i].Stop())
-
- t.Logf("Checking all members in member's backend store: %d", i)
- ensureAllMembersFromV3StoreAreVotingMembers(t, epc.Procs[i].Config().DataDirPath)
-
- t.Logf("Starting the member again: %d", i)
- require.NoError(t, epc.Procs[i].Start(ctx))
- }
- })
- }
-}
-
-func mustCreateNewClusterByPromotingMembers(t *testing.T, clusterVersion e2e.ClusterVersion, clusterSize int, opts ...e2e.EPClusterOption) (*e2e.EtcdProcessCluster, []*etcdserverpb.Member) {
- require.GreaterOrEqualf(t, clusterSize, 1, "clusterSize must be at least 1")
-
- ctx := context.Background()
-
- t.Logf("Creating new etcd cluster - version: %s, clusterSize: %v", clusterVersion, clusterSize)
- opts = append(opts, e2e.WithVersion(clusterVersion), e2e.WithClusterSize(1))
- epc, err := e2e.NewEtcdProcessCluster(ctx, t, opts...)
- require.NoErrorf(t, err, "failed to start first etcd process")
- defer func() {
- if t.Failed() {
- epc.Close()
- }
- }()
- var promotedMembers []*etcdserverpb.Member
- for i := 1; i < clusterSize; i++ {
- var (
- memberID uint64
- aerr error
- )
-
- // NOTE: New promoted member needs time to get connected.
- t.Logf("[%d] Adding new member as learner", i)
- testutils.ExecuteWithTimeout(t, 1*time.Minute, func() {
- for {
- memberID, aerr = epc.StartNewProc(ctx, nil, t, true)
- if aerr != nil {
- if strings.Contains(aerr.Error(), "etcdserver: unhealthy cluster") {
- time.Sleep(1 * time.Second)
- continue
- }
- }
- break
+ t.Logf("Killing %d member", tc.downMembers)
+ for i := 0; i < tc.downMembers; i++ {
+ t.Logf("Killing member, name: %s, peerURL: %s", epc.Procs[i].Config().Name, epc.Procs[i].Config().PeerURL.String())
+ err = epc.Procs[i].Kill()
+ require.NoError(t, err)
}
- })
- require.NoError(t, aerr)
- t.Logf("[%d] Promoting member (%x)", i, memberID)
- etcdctl := epc.Procs[0].Etcdctl()
- resp, merr := etcdctl.MemberPromote(ctx, memberID)
- require.NoError(t, merr)
+ time.Sleep(etcdserver.HealthInterval + 2*time.Second)
- for _, m := range resp.Members {
- if m.ID == memberID {
- promotedMembers = append(promotedMembers, m)
+ t.Log("Adding a new learner")
+ _, err = epc.Procs[len(epc.Procs)-1].Etcdctl().MemberAddAsLearner(ctx, "new-learner", []string{"http://10.0.0.12:2380"})
+ if tc.expectErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
}
- }
- }
-
- t.Log("Ensure all members are voting members from user perspective")
- ensureAllMembersAreVotingMembers(t, epc)
-
- return epc, promotedMembers
-}
-
-func mustSaveMemberIntoBbolt(t *testing.T, dataDir string, protoMember *etcdserverpb.Member) {
- dbPath := datadir.ToBackendFileName(dataDir)
- db, err := bbolt.Open(dbPath, 0o666, nil)
- require.NoError(t, err)
- defer func() {
- require.NoError(t, db.Close())
- }()
-
- m := &membership.Member{
- ID: types.ID(protoMember.ID),
- RaftAttributes: membership.RaftAttributes{
- PeerURLs: protoMember.PeerURLs,
- IsLearner: protoMember.IsLearner,
- },
- Attributes: membership.Attributes{
- Name: protoMember.Name,
- ClientURLs: protoMember.ClientURLs,
- },
- }
-
- err = db.Update(func(tx *bbolt.Tx) error {
- b := tx.Bucket(schema.Members.Name())
-
- mkey := []byte(m.ID.String())
- mvalue, jerr := json.Marshal(m)
- require.NoError(t, jerr)
-
- return b.Put(mkey, mvalue)
- })
- require.NoError(t, err)
-}
-
-func ensureAllMembersAreVotingMembers(t *testing.T, epc *e2e.EtcdProcessCluster) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- memberListResp, err := epc.Etcdctl().MemberList(ctx, false)
- require.NoError(t, err)
- for _, m := range memberListResp.Members {
- require.False(t, m.IsLearner)
- }
-}
-
-func ensureAllMembersFromV3StoreAreVotingMembers(t *testing.T, dataDir string) {
- dbPath := datadir.ToBackendFileName(dataDir)
- db, err := bbolt.Open(dbPath, 0o400, &bbolt.Options{ReadOnly: true})
- require.NoError(t, err)
- defer func() {
- require.NoError(t, db.Close())
- }()
-
- var members []membership.Member
- _ = db.View(func(tx *bbolt.Tx) error {
- b := tx.Bucket(schema.Members.Name())
- _ = b.ForEach(func(k, v []byte) error {
- m := membership.Member{}
- jerr := json.Unmarshal(v, &m)
- require.NoError(t, jerr)
- members = append(members, m)
- return nil
})
- return nil
- })
-
- for _, m := range members {
- require.Falsef(t, m.IsLearner, "member is still learner: %+v", m)
}
}
diff --git a/tests/e2e/ctl_v3_move_leader_test.go b/tests/e2e/ctl_v3_move_leader_test.go
index cc3e80774434..c77beb2b8b8d 100644
--- a/tests/e2e/ctl_v3_move_leader_test.go
+++ b/tests/e2e/ctl_v3_move_leader_test.go
@@ -84,7 +84,7 @@ func testCtlV3MoveLeader(t *testing.T, cfg e2e.EtcdProcessClusterConfig, envVars
TLS: tcfg,
})
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
resp, err := cli.Status(ctx, ep)
if err != nil {
t.Fatalf("failed to get status from endpoint %s: %v", ep, err)
@@ -145,7 +145,7 @@ func setupEtcdctlTest(t *testing.T, cfg *e2e.EtcdProcessClusterConfig, quorum bo
if !quorum {
cfg = e2e.ConfigStandalone(*cfg)
}
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
if err != nil {
t.Fatalf("could not start etcd process cluster (%v)", err)
}
diff --git a/tests/e2e/ctl_v3_snapshot_test.go b/tests/e2e/ctl_v3_snapshot_test.go
index c9dc03235d17..97dbb7a2901f 100644
--- a/tests/e2e/ctl_v3_snapshot_test.go
+++ b/tests/e2e/ctl_v3_snapshot_test.go
@@ -163,17 +163,13 @@ func TestIssue6361(t *testing.T) { testIssue6361(t) }
// TestIssue6361 ensures new member that starts with snapshot correctly
// syncs up with other members and serve correct data.
func testIssue6361(t *testing.T) {
- {
- // This tests is pretty flaky on semaphoreci as of 2021-01-10.
- // TODO: Remove when the flakiness source is identified.
- oldenv := os.Getenv("EXPECT_DEBUG")
- defer os.Setenv("EXPECT_DEBUG", oldenv)
- os.Setenv("EXPECT_DEBUG", "1")
- }
+ // This tests is pretty flaky on semaphoreci as of 2021-01-10.
+ // TODO: Remove when the flakiness source is identified.
+ t.Setenv("EXPECT_DEBUG", "1")
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(1),
e2e.WithKeepDataDir(true),
)
@@ -226,7 +222,7 @@ func testIssue6361(t *testing.T) {
epc.Procs[0].Config().Args[i+1] = newDataDir
}
}
- require.NoError(t, epc.Procs[0].Restart(context.TODO()))
+ require.NoError(t, epc.Procs[0].Restart(t.Context()))
t.Log("Ensuring the restored member has the correct data...")
for i := range kvs {
@@ -290,15 +286,15 @@ func snapshotVersionTest(cx ctlCtx) {
if err != nil {
cx.t.Fatalf("snapshotVersionTest getSnapshotStatus error (%v)", err)
}
- if st.Version != "3.6.0" {
- cx.t.Fatalf("expected %q, got %q", "3.6.0", st.Version)
+ if st.Version != "3.7.0" {
+ cx.t.Fatalf("expected %q, got %q", "3.7.0", st.Version)
}
}
func TestRestoreCompactionRevBump(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(1),
e2e.WithKeepDataDir(true),
)
@@ -313,13 +309,14 @@ func TestRestoreCompactionRevBump(t *testing.T) {
ctl := epc.Etcdctl()
- watchCh := ctl.Watch(context.Background(), "foo", config.WatchOptions{Prefix: true})
+ watchCh := ctl.Watch(t.Context(), "foo", config.WatchOptions{Prefix: true})
// flake-fix: the watch can sometimes miss the first put below causing test failure
time.Sleep(100 * time.Millisecond)
kvs := []testutils.KV{{Key: "foo1", Val: "val1"}, {Key: "foo2", Val: "val2"}, {Key: "foo3", Val: "val3"}}
for i := range kvs {
- require.NoError(t, ctl.Put(context.Background(), kvs[i].Key, kvs[i].Val, config.PutOptions{}))
+ _, err = ctl.Put(t.Context(), kvs[i].Key, kvs[i].Val, config.PutOptions{})
+ require.NoError(t, err)
}
watchTimeout := 1 * time.Second
@@ -341,10 +338,11 @@ func TestRestoreCompactionRevBump(t *testing.T) {
// add some more kvs that are not in the snapshot that will be lost after restore
unsnappedKVs := []testutils.KV{{Key: "unsnapped1", Val: "one"}, {Key: "unsnapped2", Val: "two"}, {Key: "unsnapped3", Val: "three"}}
for i := range unsnappedKVs {
- require.NoError(t, ctl.Put(context.Background(), unsnappedKVs[i].Key, unsnappedKVs[i].Val, config.PutOptions{}))
+ _, err = ctl.Put(t.Context(), unsnappedKVs[i].Key, unsnappedKVs[i].Val, config.PutOptions{})
+ require.NoError(t, err)
}
- membersBefore, err := ctl.MemberList(context.Background(), false)
+ membersBefore, err := ctl.MemberList(t.Context(), false)
require.NoError(t, err)
t.Log("Stopping the original server...")
@@ -369,21 +367,18 @@ func TestRestoreCompactionRevBump(t *testing.T) {
t.Log("(Re)starting the etcd member using the restored snapshot...")
epc.Procs[0].Config().DataDirPath = newDataDir
- for i := range epc.Procs[0].Config().Args {
- if epc.Procs[0].Config().Args[i] == "--data-dir" {
- epc.Procs[0].Config().Args[i+1] = newDataDir
- }
- }
+ err = e2e.PatchArgs(epc.Procs[0].Config().Args, "data-dir", newDataDir)
+ require.NoError(t, err)
// Verify that initial snapshot is created by the restore operation
verifySnapshotMembers(t, epc, membersBefore)
- require.NoError(t, epc.Restart(context.Background()))
+ require.NoError(t, epc.Restart(t.Context()))
t.Log("Ensuring the restored member has the correct data...")
hasKVs(t, ctl, kvs, currentRev, baseRev)
for i := range unsnappedKVs {
- v, gerr := ctl.Get(context.Background(), unsnappedKVs[i].Key, config.GetOptions{})
+ v, gerr := ctl.Get(t.Context(), unsnappedKVs[i].Key, config.GetOptions{})
require.NoError(t, gerr)
require.Equal(t, int64(0), v.Count)
}
@@ -399,7 +394,7 @@ func TestRestoreCompactionRevBump(t *testing.T) {
// clients might restart the watch at the old base revision, that should not yield any new data
// everything up until bumpAmount+currentRev should return "already compacted"
for i := bumpAmount - 2; i < bumpAmount+currentRev; i++ {
- watchCh = ctl.Watch(context.Background(), "foo", config.WatchOptions{Prefix: true, Revision: int64(i)})
+ watchCh = ctl.Watch(t.Context(), "foo", config.WatchOptions{Prefix: true, Revision: int64(i)})
cancelResult := <-watchCh
require.Equal(t, v3rpc.ErrCompacted, cancelResult.Err())
require.Truef(t, cancelResult.Canceled, "expected ongoing watch to be cancelled after restoring with --mark-compacted")
@@ -407,10 +402,11 @@ func TestRestoreCompactionRevBump(t *testing.T) {
}
// a watch after that revision should yield successful results when a new put arrives
- ctx, cancel := context.WithTimeout(context.Background(), watchTimeout*5)
+ ctx, cancel := context.WithTimeout(t.Context(), watchTimeout*5)
defer cancel()
watchCh = ctl.Watch(ctx, "foo", config.WatchOptions{Prefix: true, Revision: int64(bumpAmount + currentRev + 1)})
- require.NoError(t, ctl.Put(context.Background(), "foo4", "val4", config.PutOptions{}))
+ _, err = ctl.Put(ctx, "foo4", "val4", config.PutOptions{})
+ require.NoError(t, err)
watchRes, err = testutils.KeyValuesFromWatchChan(watchCh, 1, watchTimeout)
require.NoErrorf(t, err, "failed to get key-values from watch channel %s", err)
require.Equal(t, []testutils.KV{{Key: "foo4", Val: "val4"}}, watchRes)
@@ -418,7 +414,7 @@ func TestRestoreCompactionRevBump(t *testing.T) {
func hasKVs(t *testing.T, ctl *e2e.EtcdctlV3, kvs []testutils.KV, currentRev int, baseRev int) {
for i := range kvs {
- v, err := ctl.Get(context.Background(), kvs[i].Key, config.GetOptions{})
+ v, err := ctl.Get(t.Context(), kvs[i].Key, config.GetOptions{})
require.NoError(t, err)
require.Equal(t, int64(1), v.Count)
require.Equal(t, kvs[i].Val, string(v.Kvs[0].Value))
@@ -431,7 +427,7 @@ func hasKVs(t *testing.T, ctl *e2e.EtcdctlV3, kvs []testutils.KV, currentRev int
func TestBreakConsistentIndexNewerThanSnapshot(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
var snapshotCount uint64 = 50
diff --git a/tests/e2e/ctl_v3_test.go b/tests/e2e/ctl_v3_test.go
index 2cd112dc6045..51334915071d 100644
--- a/tests/e2e/ctl_v3_test.go
+++ b/tests/e2e/ctl_v3_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"fmt"
"os"
"strings"
@@ -28,6 +27,7 @@ import (
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/testutil"
"go.etcd.io/etcd/pkg/v3/expect"
+ "go.etcd.io/etcd/pkg/v3/featuregate"
"go.etcd.io/etcd/pkg/v3/flags"
"go.etcd.io/etcd/tests/v3/framework/e2e"
)
@@ -60,7 +60,7 @@ func TestClusterVersion(t *testing.T) {
e2e.WithRollingStart(tt.rollingStart),
)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
if err != nil {
t.Fatalf("could not start etcd process cluster (%v)", err)
}
@@ -226,13 +226,13 @@ func testCtlWithOffline(t *testing.T, testFunc func(ctlCtx), testOfflineFunc fun
}
ret.cfg.ServerConfig.StrictReconfigCheck = !ret.disableStrictReconfigCheck
if ret.initialCorruptCheck {
- ret.cfg.ServerConfig.ExperimentalInitialCorruptCheck = ret.initialCorruptCheck
+ ret.cfg.ServerConfig.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("InitialCorruptCheck=%t", ret.initialCorruptCheck))
}
if testOfflineFunc != nil {
ret.cfg.KeepDataDir = true
}
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(&ret.cfg))
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(&ret.cfg))
if err != nil {
t.Fatalf("could not start etcd process cluster (%v)", err)
}
diff --git a/tests/e2e/ctl_v3_watch_no_cov_test.go b/tests/e2e/ctl_v3_watch_no_cov_test.go
deleted file mode 100644
index 9df7cc60078a..000000000000
--- a/tests/e2e/ctl_v3_watch_no_cov_test.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2018 The etcd Authors
-//
-// 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 e2e
-
-import (
- "os"
- "testing"
-
- "go.etcd.io/etcd/tests/v3/framework/e2e"
-)
-
-func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) }
-func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigNoTLS())) }
-func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigClientTLS())) }
-func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigPeerTLS())) }
-func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDefaultDialTimeout()) }
-
-func TestCtlV3WatchInteractive(t *testing.T) {
- testCtl(t, watchTest, withInteractive())
-}
-
-func TestCtlV3WatchInteractiveNoTLS(t *testing.T) {
- testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigNoTLS()))
-}
-
-func TestCtlV3WatchInteractiveClientTLS(t *testing.T) {
- testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigClientTLS()))
-}
-
-func TestCtlV3WatchInteractivePeerTLS(t *testing.T) {
- testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigPeerTLS()))
-}
-
-func watchTest(cx ctlCtx) {
- tests := []struct {
- puts []kv
- envKey string
- envRange string
- args []string
-
- wkv []kvExec
- }{
- { // watch 1 key with env
- puts: []kv{{"sample", "value"}},
- envKey: "sample",
- args: []string{"--rev", "1"},
- wkv: []kvExec{{key: "sample", val: "value"}},
- },
- { // watch 1 key with ${ETCD_WATCH_VALUE}
- puts: []kv{{"sample", "value"}},
- args: []string{"sample", "--rev", "1", "--", "env"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: `ETCD_WATCH_VALUE="value"`}},
- },
- { // watch 1 key with "echo watch event received", with env
- puts: []kv{{"sample", "value"}},
- envKey: "sample",
- args: []string{"--rev", "1", "--", "echo", "watch event received"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
- },
- { // watch 1 key with "echo watch event received"
- puts: []kv{{"sample", "value"}},
- args: []string{"--rev", "1", "sample", "--", "echo", "watch event received"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
- },
- { // watch 1 key with "echo \"Hello World!\""
- puts: []kv{{"sample", "value"}},
- args: []string{"--rev", "1", "sample", "--", "echo", "\"Hello World!\""},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "Hello World!"}},
- },
- { // watch 1 key with "echo watch event received"
- puts: []kv{{"sample", "value"}},
- args: []string{"sample", "samplx", "--rev", "1", "--", "echo", "watch event received"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
- },
- { // watch 1 key with "echo watch event received"
- puts: []kv{{"sample", "value"}},
- envKey: "sample",
- envRange: "samplx",
- args: []string{"--rev", "1", "--", "echo", "watch event received"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
- },
- { // watch 1 key with "echo watch event received"
- puts: []kv{{"sample", "value"}},
- args: []string{"sample", "--rev", "1", "samplx", "--", "echo", "watch event received"},
- wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
- },
- { // watch 3 keys by prefix, with env
- puts: []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
- envKey: "key",
- args: []string{"--rev", "1", "--prefix"},
- wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}, {key: "key3", val: "val3"}},
- },
- { // watch 3 keys by range, with env
- puts: []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
- envKey: "key",
- envRange: "key3",
- args: []string{"--rev", "1"},
- wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
- },
- }
-
- for i, tt := range tests {
- donec := make(chan struct{})
- go func(i int, puts []kv) {
- for j := range puts {
- if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
- cx.t.Errorf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
- }
- }
- close(donec)
- }(i, tt.puts)
-
- unsetEnv := func() {}
- if tt.envKey != "" || tt.envRange != "" {
- if tt.envKey != "" {
- os.Setenv("ETCDCTL_WATCH_KEY", tt.envKey)
- unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_KEY") }
- }
- if tt.envRange != "" {
- os.Setenv("ETCDCTL_WATCH_RANGE_END", tt.envRange)
- unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_RANGE_END") }
- }
- if tt.envKey != "" && tt.envRange != "" {
- unsetEnv = func() {
- os.Unsetenv("ETCDCTL_WATCH_KEY")
- os.Unsetenv("ETCDCTL_WATCH_RANGE_END")
- }
- }
- }
- if err := ctlV3Watch(cx, tt.args, tt.wkv...); err != nil {
- if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
- cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err)
- }
- }
- unsetEnv()
- <-donec
- }
-}
diff --git a/tests/e2e/ctl_v3_watch_test.go b/tests/e2e/ctl_v3_watch_test.go
index bec43224e4c6..85bf82f90700 100644
--- a/tests/e2e/ctl_v3_watch_test.go
+++ b/tests/e2e/ctl_v3_watch_test.go
@@ -15,7 +15,9 @@
package e2e
import (
+ "os"
"strings"
+ "testing"
"go.etcd.io/etcd/tests/v3/framework/e2e"
)
@@ -91,3 +93,131 @@ func ctlV3WatchFailPerm(cx ctlCtx, args []string) error {
}
return proc.Close()
}
+
+func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) }
+func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigNoTLS())) }
+func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigClientTLS())) }
+func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(*e2e.NewConfigPeerTLS())) }
+func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDefaultDialTimeout()) }
+
+func TestCtlV3WatchInteractive(t *testing.T) {
+ testCtl(t, watchTest, withInteractive())
+}
+
+func TestCtlV3WatchInteractiveNoTLS(t *testing.T) {
+ testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigNoTLS()))
+}
+
+func TestCtlV3WatchInteractiveClientTLS(t *testing.T) {
+ testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigClientTLS()))
+}
+
+func TestCtlV3WatchInteractivePeerTLS(t *testing.T) {
+ testCtl(t, watchTest, withInteractive(), withCfg(*e2e.NewConfigPeerTLS()))
+}
+
+func watchTest(cx ctlCtx) {
+ tests := []struct {
+ puts []kv
+ envKey string
+ envRange string
+ args []string
+
+ wkv []kvExec
+ }{
+ { // watch 1 key with env
+ puts: []kv{{"sample", "value"}},
+ envKey: "sample",
+ args: []string{"--rev", "1"},
+ wkv: []kvExec{{key: "sample", val: "value"}},
+ },
+ { // watch 1 key with ${ETCD_WATCH_VALUE}
+ puts: []kv{{"sample", "value"}},
+ args: []string{"sample", "--rev", "1", "--", "env"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: `ETCD_WATCH_VALUE="value"`}},
+ },
+ { // watch 1 key with "echo watch event received", with env
+ puts: []kv{{"sample", "value"}},
+ envKey: "sample",
+ args: []string{"--rev", "1", "--", "echo", "watch event received"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
+ },
+ { // watch 1 key with "echo watch event received"
+ puts: []kv{{"sample", "value"}},
+ args: []string{"--rev", "1", "sample", "--", "echo", "watch event received"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
+ },
+ { // watch 1 key with "echo \"Hello World!\""
+ puts: []kv{{"sample", "value"}},
+ args: []string{"--rev", "1", "sample", "--", "echo", "\"Hello World!\""},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "Hello World!"}},
+ },
+ { // watch 1 key with "echo watch event received"
+ puts: []kv{{"sample", "value"}},
+ args: []string{"sample", "samplx", "--rev", "1", "--", "echo", "watch event received"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
+ },
+ { // watch 1 key with "echo watch event received"
+ puts: []kv{{"sample", "value"}},
+ envKey: "sample",
+ envRange: "samplx",
+ args: []string{"--rev", "1", "--", "echo", "watch event received"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
+ },
+ { // watch 1 key with "echo watch event received"
+ puts: []kv{{"sample", "value"}},
+ args: []string{"sample", "--rev", "1", "samplx", "--", "echo", "watch event received"},
+ wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
+ },
+ { // watch 3 keys by prefix, with env
+ puts: []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
+ envKey: "key",
+ args: []string{"--rev", "1", "--prefix"},
+ wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}, {key: "key3", val: "val3"}},
+ },
+ { // watch 3 keys by range, with env
+ puts: []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
+ envKey: "key",
+ envRange: "key3",
+ args: []string{"--rev", "1"},
+ wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
+ },
+ }
+
+ for i, tt := range tests {
+ donec := make(chan struct{})
+ go func(i int, puts []kv) {
+ for j := range puts {
+ if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
+ cx.t.Errorf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
+ }
+ }
+ close(donec)
+ }(i, tt.puts)
+
+ unsetEnv := func() {}
+ if tt.envKey != "" || tt.envRange != "" {
+ if tt.envKey != "" {
+ os.Setenv("ETCDCTL_WATCH_KEY", tt.envKey)
+ unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_KEY") }
+ }
+ if tt.envRange != "" {
+ os.Setenv("ETCDCTL_WATCH_RANGE_END", tt.envRange)
+ unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_RANGE_END") }
+ }
+ if tt.envKey != "" && tt.envRange != "" {
+ unsetEnv = func() {
+ os.Unsetenv("ETCDCTL_WATCH_KEY")
+ os.Unsetenv("ETCDCTL_WATCH_RANGE_END")
+ }
+ }
+ }
+ if err := ctlV3Watch(cx, tt.args, tt.wkv...); err != nil {
+ if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
+ cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err)
+ }
+ }
+ unsetEnv()
+ <-donec
+ }
+}
diff --git a/tests/e2e/defrag_no_space_test.go b/tests/e2e/defrag_no_space_test.go
index f6ceabe667b3..57953efe2dae 100644
--- a/tests/e2e/defrag_no_space_test.go
+++ b/tests/e2e/defrag_no_space_test.go
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"fmt"
"testing"
"time"
@@ -48,7 +47,7 @@ func TestDefragNoSpace(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
e2e.BeforeTest(t)
- clus, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(1),
e2e.WithGoFailEnabled(true),
)
@@ -57,12 +56,14 @@ func TestDefragNoSpace(t *testing.T) {
member := clus.Procs[0]
- require.NoError(t, member.Failpoints().SetupHTTP(context.Background(), tc.failpoint, fmt.Sprintf(`return("%s")`, tc.err)))
- require.ErrorContains(t, member.Etcdctl().Defragment(context.Background(), config.DefragOption{Timeout: time.Minute}), tc.err)
+ require.NoError(t, member.Failpoints().SetupHTTP(t.Context(), tc.failpoint, fmt.Sprintf(`return("%s")`, tc.err)))
+ require.ErrorContains(t, member.Etcdctl().Defragment(t.Context(), config.DefragOption{Timeout: time.Minute}), tc.err)
// Make sure etcd continues to run even after the failed defrag attempt
- require.NoError(t, member.Etcdctl().Put(context.Background(), "foo", "bar", config.PutOptions{}))
- value, err := member.Etcdctl().Get(context.Background(), "foo", config.GetOptions{})
+
+ _, err = member.Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
+ value, err := member.Etcdctl().Get(t.Context(), "foo", config.GetOptions{})
require.NoError(t, err)
require.Len(t, value.Kvs, 1)
require.Equal(t, "bar", string(value.Kvs[0].Value))
diff --git a/tests/e2e/discovery_test.go b/tests/e2e/discovery_test.go
deleted file mode 100644
index 15f51a9572d6..000000000000
--- a/tests/e2e/discovery_test.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2022 The etcd Authors
-//
-// 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.
-
-//go:build !cluster_proxy
-
-package e2e
-
-import (
- "context"
- "fmt"
- "net/http"
- "strings"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-
- "go.etcd.io/etcd/client/pkg/v3/fileutil"
- "go.etcd.io/etcd/client/pkg/v3/testutil"
- "go.etcd.io/etcd/client/pkg/v3/transport"
- "go.etcd.io/etcd/client/v2"
- "go.etcd.io/etcd/pkg/v3/expect"
- "go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp"
- "go.etcd.io/etcd/tests/v3/framework/e2e"
- "go.etcd.io/etcd/tests/v3/framework/integration"
-)
-
-func TestClusterOf1UsingDiscovery(t *testing.T) { testClusterUsingDiscovery(t, 1, false) }
-func TestClusterOf3UsingDiscovery(t *testing.T) { testClusterUsingDiscovery(t, 3, false) }
-func TestTLSClusterOf3UsingDiscovery(t *testing.T) { testClusterUsingDiscovery(t, 3, true) }
-
-func testClusterUsingDiscovery(t *testing.T, size int, peerTLS bool) {
- e2e.BeforeTest(t)
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
-
- dc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
- e2e.WithBasePort(2000),
- e2e.WithVersion(e2e.LastVersion),
- e2e.WithClusterSize(1),
- e2e.WithEnableV2(true),
- )
- if err != nil {
- t.Fatalf("could not start etcd process cluster (%v)", err)
- }
- defer dc.Close()
-
- dcc := MustNewHTTPClient(t, dc.EndpointsHTTP(), nil)
- dkapi := client.NewKeysAPI(dcc)
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
- _, err = dkapi.Create(ctx, "/_config/size", fmt.Sprintf("%d", size))
- require.NoError(t, err)
- cancel()
-
- c, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
- e2e.WithBasePort(3000),
- e2e.WithClusterSize(size),
- e2e.WithIsPeerTLS(peerTLS),
- e2e.WithDiscovery(dc.EndpointsHTTP()[0]+"/v2/keys"),
- )
- if err != nil {
- t.Fatalf("could not start etcd process cluster (%v)", err)
- }
- defer c.Close()
-
- kubectl := []string{e2e.BinPath.Etcdctl, "--endpoints", strings.Join(c.EndpointsGRPC(), ",")}
- require.NoError(t, e2e.SpawnWithExpect(append(kubectl, "put", "key", "value"), expect.ExpectedResponse{Value: "OK"}))
- require.NoError(t, e2e.SpawnWithExpect(append(kubectl, "get", "key"), expect.ExpectedResponse{Value: "value"}))
-}
-
-func MustNewHTTPClient(t testutil.TB, eps []string, tls *transport.TLSInfo) client.Client {
- cfgtls := transport.TLSInfo{}
- if tls != nil {
- cfgtls = *tls
- }
- cfg := client.Config{Transport: mustNewTransport(t, cfgtls), Endpoints: eps}
- c, err := client.New(cfg)
- require.NoError(t, err)
- return c
-}
-
-func mustNewTransport(t testutil.TB, tlsInfo transport.TLSInfo) *http.Transport {
- // tick in integration test is short, so 1s dial timeout could play well.
- tr, err := transport.NewTimeoutTransport(tlsInfo, time.Second, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
- require.NoError(t, err)
- return tr
-}
diff --git a/tests/e2e/discovery_v3_test.go b/tests/e2e/discovery_v3_test.go
index f3c47dd34e59..384f294cbfe9 100644
--- a/tests/e2e/discovery_v3_test.go
+++ b/tests/e2e/discovery_v3_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"fmt"
"strconv"
"strings"
@@ -55,7 +54,7 @@ func testClusterUsingV3Discovery(t *testing.T, discoveryClusterSize, targetClust
e2e.BeforeTest(t)
// step 1: start the discovery service
- ds, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ ds, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithBasePort(2000),
e2e.WithClusterSize(discoveryClusterSize),
e2e.WithClientConnType(clientTLSType),
@@ -122,5 +121,5 @@ func bootstrapEtcdClusterUsingV3Discovery(t *testing.T, discoveryEndpoints []str
}
// start the cluster
- return e2e.StartEtcdProcessCluster(context.TODO(), t, epc, cfg)
+ return e2e.StartEtcdProcessCluster(t.Context(), t, epc, cfg)
}
diff --git a/tests/e2e/etcd_config_test.go b/tests/e2e/etcd_config_test.go
index 229b2c5f60eb..8cccb24eb294 100644
--- a/tests/e2e/etcd_config_test.go
+++ b/tests/e2e/etcd_config_test.go
@@ -40,7 +40,7 @@ func TestEtcdExampleConfig(t *testing.T) {
proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--config-file", exampleConfigFile}, nil)
require.NoError(t, err)
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, e2e.EtcdServerReadyLines))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, e2e.EtcdServerReadyLines))
require.NoError(t, proc.Stop())
}
@@ -80,7 +80,7 @@ func TestEtcdMultiPeer(t *testing.T) {
}
for _, p := range procs {
- err := e2e.WaitReadyExpectProc(context.TODO(), p, e2e.EtcdServerReadyLines)
+ err := e2e.WaitReadyExpectProc(t.Context(), p, e2e.EtcdServerReadyLines)
require.NoError(t, err)
}
}
@@ -102,7 +102,7 @@ func TestEtcdUnixPeers(t *testing.T) {
)
defer os.Remove("etcd.unix:1")
require.NoError(t, err)
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, e2e.EtcdServerReadyLines))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, e2e.EtcdServerReadyLines))
require.NoError(t, proc.Stop())
}
@@ -150,7 +150,7 @@ func TestEtcdListenMetricsURLsWithMissingClientTLSInfo(t *testing.T) {
_ = proc.Close()
}()
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{embed.ErrMissingClientTLSInfoForMetricsURL.Error()}))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, []string{embed.ErrMissingClientTLSInfoForMetricsURL.Error()}))
}
// TestEtcdPeerCNAuth checks that the inter peer auth based on CN of cert is working correctly.
@@ -224,7 +224,7 @@ func TestEtcdPeerCNAuth(t *testing.T) {
} else {
expect = []string{"remote error: tls: bad certificate"}
}
- err := e2e.WaitReadyExpectProc(context.TODO(), p, expect)
+ err := e2e.WaitReadyExpectProc(t.Context(), p, expect)
require.NoError(t, err)
}
}
@@ -311,7 +311,7 @@ func TestEtcdPeerMultiCNAuth(t *testing.T) {
} else {
expect = []string{"remote error: tls: bad certificate"}
}
- err := e2e.WaitReadyExpectProc(context.TODO(), p, expect)
+ err := e2e.WaitReadyExpectProc(t.Context(), p, expect)
require.NoError(t, err)
}
}
@@ -384,7 +384,7 @@ func TestEtcdPeerNameAuth(t *testing.T) {
} else {
expect = []string{"client certificate authentication failed"}
}
- err := e2e.WaitReadyExpectProc(context.TODO(), p, expect)
+ err := e2e.WaitReadyExpectProc(t.Context(), p, expect)
require.NoError(t, err)
}
}
@@ -490,7 +490,7 @@ func TestEtcdPeerLocalAddr(t *testing.T) {
} else {
expect = []string{"x509: certificate is valid for 127.0.0.1, not "}
}
- err := e2e.WaitReadyExpectProc(context.TODO(), p, expect)
+ err := e2e.WaitReadyExpectProc(t.Context(), p, expect)
require.NoError(t, err)
}
}
@@ -568,9 +568,9 @@ func TestGrpcproxyAndListenCipherSuite(t *testing.T) {
func TestBootstrapDefragFlag(t *testing.T) {
e2e.SkipInShortMode(t)
- proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--experimental-bootstrap-defrag-threshold-megabytes", "1000"}, nil)
+ proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--bootstrap-defrag-threshold-megabytes", "1000"}, nil)
require.NoError(t, err)
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{"Skipping defragmentation"}))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, []string{"Skipping defragmentation"}))
require.NoError(t, proc.Stop())
// wait for the process to exit, otherwise test will have leaked goroutine
@@ -582,10 +582,10 @@ func TestBootstrapDefragFlag(t *testing.T) {
func TestSnapshotCatchupEntriesFlag(t *testing.T) {
e2e.SkipInShortMode(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
- proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--experimental-snapshot-catchup-entries", "1000"}, nil)
+ proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--snapshot-catchup-entries", "1000"}, nil)
require.NoError(t, err)
require.NoError(t, e2e.WaitReadyExpectProc(ctx, proc, []string{"\"snapshot-catchup-entries\":1000"}))
require.NoError(t, e2e.WaitReadyExpectProc(ctx, proc, []string{"serving client traffic"}))
@@ -600,7 +600,7 @@ func TestSnapshotCatchupEntriesFlag(t *testing.T) {
// TestEtcdHealthyWithTinySnapshotCatchupEntries ensures multi-node etcd cluster remains healthy with 1 snapshot catch up entry
func TestEtcdHealthyWithTinySnapshotCatchupEntries(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(3),
e2e.WithSnapshotCount(1),
e2e.WithSnapshotCatchUpEntries(1),
@@ -613,7 +613,7 @@ func TestEtcdHealthyWithTinySnapshotCatchupEntries(t *testing.T) {
})
// simulate 10 clients keep writing to etcd in parallel with no error
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
g, ctx := errgroup.WithContext(ctx)
for i := 0; i < 10; i++ {
@@ -621,7 +621,7 @@ func TestEtcdHealthyWithTinySnapshotCatchupEntries(t *testing.T) {
g.Go(func() error {
cc := epc.Etcdctl()
for j := 0; j < 100; j++ {
- if err := cc.Put(ctx, "foo", fmt.Sprintf("bar%d", clientID), config.PutOptions{}); err != nil {
+ if _, err := cc.Put(ctx, "foo", fmt.Sprintf("bar%d", clientID), config.PutOptions{}); err != nil {
return err
}
}
@@ -655,7 +655,7 @@ func TestEtcdTLSVersion(t *testing.T) {
}, nil,
)
assert.NoError(t, err)
- assert.NoErrorf(t, e2e.WaitReadyExpectProc(context.TODO(), proc, e2e.EtcdServerReadyLines), "did not receive expected output from etcd process")
+ assert.NoErrorf(t, e2e.WaitReadyExpectProc(t.Context(), proc, e2e.EtcdServerReadyLines), "did not receive expected output from etcd process")
assert.NoError(t, proc.Stop())
proc.Wait() // ensure the port has been released
@@ -694,7 +694,7 @@ func TestEtcdDeprecatedFlags(t *testing.T) {
tc.args, nil,
)
require.NoError(t, err)
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{tc.expectedMsg}))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, []string{tc.expectedMsg}))
require.NoError(t, proc.Stop())
proc.Wait() // ensure the port has been released
@@ -722,7 +722,7 @@ func TestV2DeprecationEnforceDefaultValue(t *testing.T) {
append(commonArgs, "--v2-deprecation", optionLevel), nil,
)
require.NoError(t, err)
- require.NoError(t, e2e.WaitReadyExpectProc(context.TODO(), proc, []string{expectedDeprecationLevelMsg}))
+ require.NoError(t, e2e.WaitReadyExpectProc(t.Context(), proc, []string{expectedDeprecationLevelMsg}))
require.NoError(t, proc.Stop())
proc.Wait() // ensure the port has been released
diff --git a/tests/e2e/etcd_grpcproxy_test.go b/tests/e2e/etcd_grpcproxy_test.go
index 83de9335f9be..7b27991b7b31 100644
--- a/tests/e2e/etcd_grpcproxy_test.go
+++ b/tests/e2e/etcd_grpcproxy_test.go
@@ -33,7 +33,7 @@ import (
func TestGrpcProxyAutoSync(t *testing.T) {
e2e.SkipInShortMode(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithClusterSize(1))
@@ -61,7 +61,7 @@ func TestGrpcProxyAutoSync(t *testing.T) {
proxyCtl, err := e2e.NewEtcdctl(e2e.ClientConfig{}, []string{proxyClientURL})
require.NoError(t, err)
- err = proxyCtl.Put(ctx, "k1", "v1", config.PutOptions{})
+ _, err = proxyCtl.Put(ctx, "k1", "v1", config.PutOptions{})
require.NoError(t, err)
// Add and start second member
@@ -93,7 +93,7 @@ func TestGrpcProxyAutoSync(t *testing.T) {
func TestGrpcProxyTLSVersions(t *testing.T) {
e2e.SkipInShortMode(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithClusterSize(1))
diff --git a/tests/e2e/etcd_mix_versions_test.go b/tests/e2e/etcd_mix_versions_test.go
index 46dab60db71b..b07d9cff3d10 100644
--- a/tests/e2e/etcd_mix_versions_test.go
+++ b/tests/e2e/etcd_mix_versions_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"fmt"
"testing"
"time"
@@ -89,7 +88,7 @@ func mixVersionsSnapshotTestByAddingMember(t *testing.T, cfg *e2e.EtcdProcessClu
}
t.Logf("Create an etcd cluster with %d member", cfg.ClusterSize)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithConfig(cfg),
e2e.WithSnapshotCount(10),
)
@@ -107,9 +106,9 @@ func mixVersionsSnapshotTestByAddingMember(t *testing.T, cfg *e2e.EtcdProcessClu
newCfg.Version = newInstanceVersion
newCfg.ServerConfig.SnapshotCatchUpEntries = 10
t.Log("Starting a new etcd instance")
- _, err = epc.StartNewProc(context.TODO(), &newCfg, t, false /* addAsLearner */)
+ _, err = epc.StartNewProc(t.Context(), &newCfg, t, false /* addAsLearner */)
require.NoErrorf(t, err, "failed to start the new etcd instance")
- defer epc.CloseProc(context.TODO(), nil)
+ defer epc.CloseProc(t.Context(), nil)
assertKVHash(t, epc)
}
@@ -136,7 +135,7 @@ func mixVersionsSnapshotTestByMockPartition(t *testing.T, cfg *e2e.EtcdProcessCl
e2e.WithSnapshotCatchUpEntries(10),
}
t.Logf("Create an etcd cluster with %d member", cfg.ClusterSize)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, clusterOptions...)
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t, clusterOptions...)
require.NoErrorf(t, err, "failed to start etcd cluster")
defer func() {
derr := epc.Close()
@@ -156,7 +155,7 @@ func mixVersionsSnapshotTestByMockPartition(t *testing.T, cfg *e2e.EtcdProcessCl
e2e.AssertProcessLogs(t, leaderEPC, "saved snapshot")
t.Log("Restart the partitioned member")
- err = toPartitionedMember.Restart(context.TODO())
+ err = toPartitionedMember.Restart(t.Context())
require.NoError(t, err)
assertKVHash(t, epc)
@@ -170,7 +169,7 @@ func writeKVs(t *testing.T, etcdctl *e2e.EtcdctlV3, startIdx, endIdx int) {
for i := startIdx; i < endIdx; i++ {
key := fmt.Sprintf("key-%d", i)
value := fmt.Sprintf("value-%d", i)
- err := etcdctl.Put(context.TODO(), key, value, config.PutOptions{})
+ _, err := etcdctl.Put(t.Context(), key, value, config.PutOptions{})
require.NoErrorf(t, err, "failed to put %q", key)
}
}
@@ -182,7 +181,7 @@ func assertKVHash(t *testing.T, epc *e2e.EtcdProcessCluster) {
}
t.Log("Verify all nodes have exact same revision and hash")
assert.Eventually(t, func() bool {
- hashKvs, err := epc.Etcdctl().HashKV(context.TODO(), 0)
+ hashKvs, err := epc.Etcdctl().HashKV(t.Context(), 0)
if err != nil {
t.Logf("failed to get HashKV: %v", err)
return false
@@ -193,7 +192,7 @@ func assertKVHash(t *testing.T, epc *e2e.EtcdProcessCluster) {
}
for i := 1; i < clusterSize; i++ {
if hashKvs[0].Header.Revision != hashKvs[i].Header.Revision {
- t.Logf("Got different revisions, [%d, %d]", hashKvs[0].Header.Revision, hashKvs[1].Header.Revision)
+ t.Logf("Got different revisions, [%d, %d]", hashKvs[0].Header.Revision, hashKvs[i].Header.Revision)
return false
}
diff --git a/tests/e2e/etcd_release_upgrade_test.go b/tests/e2e/etcd_release_upgrade_test.go
index b6448e0bcaae..a153b43e4973 100644
--- a/tests/e2e/etcd_release_upgrade_test.go
+++ b/tests/e2e/etcd_release_upgrade_test.go
@@ -17,6 +17,7 @@ package e2e
import (
"context"
"fmt"
+ "strings"
"sync"
"testing"
"time"
@@ -24,11 +25,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/version"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/pkg/v3/expect"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/e2e"
+ "go.etcd.io/etcd/tests/v3/framework/testutils"
)
// TestReleaseUpgrade ensures that changes to master branch does not affect
@@ -40,7 +43,7 @@ func TestReleaseUpgrade(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithVersion(e2e.LastVersion),
e2e.WithSnapshotCount(3),
e2e.WithBasePeerScheme("unix"), // to avoid port conflict
@@ -83,7 +86,7 @@ func TestReleaseUpgrade(t *testing.T) {
epc.Procs[i].Config().KeepDataDir = true
t.Logf("Restarting node in the new version: %v", i)
- if err = epc.Procs[i].Restart(context.TODO()); err != nil {
+ if err = epc.Procs[i].Restart(t.Context()); err != nil {
t.Fatalf("error restarting etcd process (%v)", err)
}
@@ -120,7 +123,7 @@ func TestReleaseUpgradeWithRestart(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithVersion(e2e.LastVersion),
e2e.WithSnapshotCount(10),
e2e.WithBasePeerScheme("unix"),
@@ -159,7 +162,7 @@ func TestReleaseUpgradeWithRestart(t *testing.T) {
go func(i int) {
epc.Procs[i].Config().ExecPath = e2e.BinPath.Etcd
epc.Procs[i].Config().KeepDataDir = true
- assert.NoErrorf(t, epc.Procs[i].Restart(context.TODO()), "error restarting etcd process")
+ assert.NoErrorf(t, epc.Procs[i].Restart(t.Context()), "error restarting etcd process")
wg.Done()
}(i)
}
@@ -196,7 +199,7 @@ func TestClusterUpgradeAfterPromotingMembers(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
epc, _ := mustCreateNewClusterByPromotingMembers(t, e2e.LastVersion, clusterSize,
e2e.WithSnapshotCount(uint64(tc.snapshot)))
@@ -205,7 +208,7 @@ func TestClusterUpgradeAfterPromotingMembers(t *testing.T) {
}()
for i := 0; i < tc.snapshot; i++ {
- err = epc.Etcdctl().Put(ctx, "foo", "bar", config.PutOptions{})
+ _, err = epc.Etcdctl().Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
}
@@ -217,9 +220,77 @@ func TestClusterUpgradeAfterPromotingMembers(t *testing.T) {
t.Logf("Checking all members are ready to serve client requests")
for i := 0; i < clusterSize; i++ {
- err = epc.Procs[i].Etcdctl().Put(context.Background(), "foo", "bar", config.PutOptions{})
+ _, err = epc.Procs[i].Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
require.NoError(t, err)
}
})
}
}
+
+func mustCreateNewClusterByPromotingMembers(t *testing.T, clusterVersion e2e.ClusterVersion, clusterSize int, opts ...e2e.EPClusterOption) (*e2e.EtcdProcessCluster, []*etcdserverpb.Member) {
+ require.GreaterOrEqualf(t, clusterSize, 1, "clusterSize must be at least 1")
+
+ ctx := t.Context()
+
+ t.Logf("Creating new etcd cluster - version: %s, clusterSize: %v", clusterVersion, clusterSize)
+ opts = append(opts, e2e.WithVersion(clusterVersion), e2e.WithClusterSize(1))
+ epc, err := e2e.NewEtcdProcessCluster(ctx, t, opts...)
+ require.NoErrorf(t, err, "failed to start first etcd process")
+ defer func() {
+ if t.Failed() {
+ epc.Close()
+ }
+ }()
+
+ var promotedMembers []*etcdserverpb.Member
+ for i := 1; i < clusterSize; i++ {
+ var (
+ memberID uint64
+ aerr error
+ )
+
+ // NOTE: New promoted member needs time to get connected.
+ t.Logf("[%d] Adding new member as learner", i)
+ testutils.ExecuteWithTimeout(t, 1*time.Minute, func() {
+ for {
+ memberID, aerr = epc.StartNewProc(ctx, nil, t, true)
+ if aerr != nil {
+ if strings.Contains(aerr.Error(), "etcdserver: unhealthy cluster") {
+ time.Sleep(1 * time.Second)
+ continue
+ }
+ }
+ break
+ }
+ })
+ require.NoError(t, aerr)
+
+ t.Logf("[%d] Promoting member (%x)", i, memberID)
+ etcdctl := epc.Procs[0].Etcdctl()
+ resp, merr := etcdctl.MemberPromote(ctx, memberID)
+ require.NoError(t, merr)
+
+ for _, m := range resp.Members {
+ if m.ID == memberID {
+ promotedMembers = append(promotedMembers, m)
+ }
+ }
+ }
+
+ t.Log("Ensure all members are voting members from user perspective")
+ ensureAllMembersAreVotingMembers(t, epc)
+
+ return epc, promotedMembers
+}
+
+func ensureAllMembersAreVotingMembers(t *testing.T, epc *e2e.EtcdProcessCluster) {
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
+ defer cancel()
+
+ resp, err := epc.Etcdctl().MemberList(ctx, false)
+ require.NoError(t, err)
+ require.Len(t, resp.Members, len(epc.Procs))
+ for _, m := range resp.Members {
+ require.Falsef(t, m.IsLearner, "node(%x)", m.ID)
+ }
+}
diff --git a/tests/e2e/failover_test.go b/tests/e2e/failover_test.go
index 878603673485..22411f5c8a8c 100644
--- a/tests/e2e/failover_test.go
+++ b/tests/e2e/failover_test.go
@@ -59,7 +59,7 @@ func TestFailoverOnDefrag(t *testing.T) {
name: "defrag failover happy case",
clusterOptions: []e2e.EPClusterOption{
e2e.WithClusterSize(3),
- e2e.WithExperimentalStopGRPCServiceOnDefrag(true),
+ e2e.WithServerFeatureGate("StopGRPCServiceOnDefrag", true),
e2e.WithGoFailEnabled(true),
},
gRPCDialOptions: []grpc.DialOption{
@@ -73,7 +73,7 @@ func TestFailoverOnDefrag(t *testing.T) {
name: "defrag blocks one-third of requests with stopGRPCServiceOnDefrag set to false",
clusterOptions: []e2e.EPClusterOption{
e2e.WithClusterSize(3),
- e2e.WithExperimentalStopGRPCServiceOnDefrag(false),
+ e2e.WithServerFeatureGate("StopGRPCServiceOnDefrag", false),
e2e.WithGoFailEnabled(true),
},
gRPCDialOptions: []grpc.DialOption{
@@ -87,7 +87,7 @@ func TestFailoverOnDefrag(t *testing.T) {
name: "defrag blocks one-third of requests with stopGRPCServiceOnDefrag set to true and client health check disabled",
clusterOptions: []e2e.EPClusterOption{
e2e.WithClusterSize(3),
- e2e.WithExperimentalStopGRPCServiceOnDefrag(true),
+ e2e.WithServerFeatureGate("StopGRPCServiceOnDefrag", true),
e2e.WithGoFailEnabled(true),
},
expectedMinQPS: 20,
@@ -136,7 +136,7 @@ func TestFailoverOnDefrag(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
e2e.BeforeTest(t)
- clus, cerr := e2e.NewEtcdProcessCluster(context.TODO(), t, tc.clusterOptions...)
+ clus, cerr := e2e.NewEtcdProcessCluster(t.Context(), t, tc.clusterOptions...)
require.NoError(t, cerr)
t.Cleanup(func() { clus.Stop() })
@@ -165,7 +165,7 @@ func TestFailoverOnDefrag(t *testing.T) {
return lastErr
default:
}
- getContext, cancel := context.WithTimeout(context.Background(), requestTimeout)
+ getContext, cancel := context.WithTimeout(t.Context(), requestTimeout)
_, err := clusterClient.Get(getContext, "health")
cancel()
requestVolume++
@@ -199,6 +199,6 @@ func TestFailoverOnDefrag(t *testing.T) {
}
func triggerDefrag(t *testing.T, member e2e.EtcdProcess) {
- require.NoError(t, member.Failpoints().SetupHTTP(context.Background(), "defragBeforeCopy", `sleep("10s")`))
- require.NoError(t, member.Etcdctl().Defragment(context.Background(), config.DefragOption{Timeout: time.Minute}))
+ require.NoError(t, member.Failpoints().SetupHTTP(t.Context(), "defragBeforeCopy", `sleep("10s")`))
+ require.NoError(t, member.Etcdctl().Defragment(t.Context(), config.DefragOption{Timeout: time.Minute}))
}
diff --git a/tests/e2e/force_new_cluster_test.go b/tests/e2e/force_new_cluster_test.go
index 8866c8562974..fd3b72e75a3a 100644
--- a/tests/e2e/force_new_cluster_test.go
+++ b/tests/e2e/force_new_cluster_test.go
@@ -17,7 +17,6 @@
package e2e
import (
- "context"
"encoding/json"
"strings"
"testing"
@@ -55,13 +54,12 @@ func TestForceNewCluster(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- ctx := context.Background()
epc, promotedMembers := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 5,
e2e.WithKeepDataDir(true), e2e.WithSnapshotCount(uint64(tc.snapcount)))
require.Len(t, promotedMembers, 4)
for i := 0; i < tc.snapcount; i++ {
- err := epc.Etcdctl().Put(ctx, "foo", "bar", config.PutOptions{})
+ _, err := epc.Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
require.NoError(t, err)
}
@@ -70,10 +68,10 @@ func TestForceNewCluster(t *testing.T) {
m := epc.Procs[0]
t.Logf("Forcibly create a one-member cluster with member: %s", m.Config().Name)
m.Config().Args = append(m.Config().Args, "--force-new-cluster")
- require.NoError(t, m.Start(ctx))
+ require.NoError(t, m.Start(t.Context()))
t.Log("Restarting the member")
- require.NoError(t, m.Restart(ctx))
+ require.NoError(t, m.Restart(t.Context()))
t.Log("Closing the member")
require.NoError(t, m.Close())
@@ -84,8 +82,6 @@ func TestForceNewCluster(t *testing.T) {
func TestForceNewCluster_MemberCount(t *testing.T) {
e2e.BeforeTest(t)
- ctx := context.Background()
-
epc, promotedMembers := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 3, e2e.WithKeepDataDir(true))
require.Len(t, promotedMembers, 2)
@@ -96,21 +92,21 @@ func TestForceNewCluster_MemberCount(t *testing.T) {
t.Log("Killing all the members")
require.NoError(t, epc.Kill())
- require.NoError(t, epc.Wait(ctx))
+ require.NoError(t, epc.Wait(t.Context()))
m := epc.Procs[0]
t.Logf("Forcibly create a one-member cluster with member: %s", m.Config().Name)
m.Config().Args = append(m.Config().Args, "--force-new-cluster")
- require.NoError(t, m.Start(ctx))
+ require.NoError(t, m.Start(t.Context()))
t.Log("Online checking the member count")
- mresp, merr := m.Etcdctl().MemberList(ctx, false)
+ mresp, merr := m.Etcdctl().MemberList(t.Context(), false)
require.NoError(t, merr)
require.Len(t, mresp.Members, 1)
t.Log("Closing the member")
require.NoError(t, m.Close())
- require.NoError(t, m.Wait(ctx))
+ require.NoError(t, m.Wait(t.Context()))
t.Log("Offline checking the member count")
members := mustReadMembersFromBoltDB(t, m.Config().DataDirPath)
@@ -157,7 +153,7 @@ func TestForceNewCluster_AddLearner_MemberCount(t *testing.T) {
})
for i := 0; i < tc.snapcount; i++ {
- werr := epc.Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
+ _, werr := epc.Etcdctl().Put(t.Context(), "foo", "bar", config.PutOptions{})
require.NoError(t, werr)
}
require.NoError(t, epc.Close())
diff --git a/tests/e2e/gateway_test.go b/tests/e2e/gateway_test.go
index 600207610884..8d4f44221824 100644
--- a/tests/e2e/gateway_test.go
+++ b/tests/e2e/gateway_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"strings"
"testing"
@@ -28,7 +27,7 @@ import (
var defaultGatewayEndpoint = "127.0.0.1:23790"
func TestGateway(t *testing.T) {
- ec, err := e2e.NewEtcdProcessCluster(context.TODO(), t)
+ ec, err := e2e.NewEtcdProcessCluster(t.Context(), t)
require.NoError(t, err)
defer ec.Stop()
diff --git a/tests/e2e/graceful_shutdown_test.go b/tests/e2e/graceful_shutdown_test.go
index b612a5c1b6a0..d889bfa4ca76 100644
--- a/tests/e2e/graceful_shutdown_test.go
+++ b/tests/e2e/graceful_shutdown_test.go
@@ -47,7 +47,7 @@ func TestGracefulShutdown(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
testRunner := e2e.NewE2eRunner()
testRunner.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, config.WithClusterSize(tc.clusterSize))
// clean up orphaned resources like closing member client.
diff --git a/tests/e2e/hashkv_test.go b/tests/e2e/hashkv_test.go
deleted file mode 100644
index 6c81e1cc7dd9..000000000000
--- a/tests/e2e/hashkv_test.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2024 The etcd Authors
-//
-// 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.
-
-//go:build !cluster_proxy
-
-package e2e
-
-import (
- "context"
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "go.etcd.io/etcd/client/pkg/v3/fileutil"
- clientv3 "go.etcd.io/etcd/client/v3"
- "go.etcd.io/etcd/tests/v3/framework/config"
- "go.etcd.io/etcd/tests/v3/framework/e2e"
-)
-
-func TestVerifyHashKVAfterCompact(t *testing.T) {
- scenarios := []struct {
- clusterVersion e2e.ClusterVersion
- keys []string // used for data generators
- }{
- {
- clusterVersion: e2e.CurrentVersion,
- keys: []string{"key0"},
- },
- {
- clusterVersion: e2e.CurrentVersion,
- keys: []string{"key0", "key1"},
- },
- {
- clusterVersion: e2e.QuorumLastVersion,
- keys: []string{"key0"},
- },
- {
- clusterVersion: e2e.QuorumLastVersion,
- keys: []string{"key0", "key1"},
- },
- }
-
- for _, compactedOnTombstoneRev := range []bool{false, true} {
- for _, scenario := range scenarios {
- t.Run(fmt.Sprintf("compactedOnTombstone=%v - %s - Keys=%v", compactedOnTombstoneRev, scenario.clusterVersion, scenario.keys), func(t *testing.T) {
- e2e.BeforeTest(t)
-
- if scenario.clusterVersion != e2e.CurrentVersion {
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
- }
-
- ctx := context.Background()
-
- cfg := e2e.NewConfigClientTLS()
- clus, err := e2e.NewEtcdProcessCluster(ctx, t,
- e2e.WithConfig(cfg),
- e2e.WithClusterSize(3),
- e2e.WithVersion(scenario.clusterVersion))
- require.NoError(t, err)
-
- t.Cleanup(func() { clus.Close() })
-
- tombstoneRevs, lastestRev := populateDataForHashKV(t, clus, cfg.Client, scenario.keys)
-
- compactedOnRev := tombstoneRevs[0]
-
- // If compaction revision isn't a tombstone, select a revision in the middle of two tombstones.
- if !compactedOnTombstoneRev {
- compactedOnRev = (tombstoneRevs[0] + tombstoneRevs[1]) / 2
- require.Greater(t, compactedOnRev, tombstoneRevs[0])
- require.Greater(t, tombstoneRevs[1], compactedOnRev)
- }
-
- cli, err := e2e.NewEtcdctl(cfg.Client, clus.EndpointsGRPC())
- require.NoError(t, err)
-
- t.Logf("COMPACT on rev=%d", compactedOnRev)
- _, err = cli.Compact(ctx, compactedOnRev, config.CompactOption{Physical: true})
- require.NoError(t, err)
-
- for rev := compactedOnRev; rev <= lastestRev; rev++ {
- verifyConsistentHashKVAcrossAllMembers(t, cli, rev)
- }
- })
- }
- }
-}
-
-func TestVerifyHashKVAfterTwoCompactionsOnTombstone_MixVersions(t *testing.T) {
- e2e.BeforeTest(t)
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
-
- ctx := context.Background()
-
- cfg := e2e.NewConfigClientTLS()
- clus, err := e2e.NewEtcdProcessCluster(ctx, t,
- e2e.WithConfig(cfg),
- e2e.WithClusterSize(3),
- e2e.WithVersion(e2e.QuorumLastVersion))
- require.NoError(t, err)
- t.Cleanup(func() { clus.Close() })
-
- tombstoneRevs, lastestRev := populateDataForHashKV(t, clus, cfg.Client, []string{"key0"})
-
- cli, err := e2e.NewEtcdctl(cfg.Client, clus.EndpointsGRPC())
- require.NoError(t, err)
-
- firstCompactOnRev := tombstoneRevs[0]
- t.Logf("COMPACT rev=%d", firstCompactOnRev)
- _, err = cli.Compact(ctx, firstCompactOnRev, config.CompactOption{Physical: true})
- require.NoError(t, err)
-
- secondCompactOnRev := tombstoneRevs[1]
- t.Logf("COMPACT rev=%d", secondCompactOnRev)
- _, err = cli.Compact(ctx, secondCompactOnRev, config.CompactOption{Physical: true})
- require.NoError(t, err)
-
- for rev := secondCompactOnRev; rev <= lastestRev; rev++ {
- verifyConsistentHashKVAcrossAllMembers(t, cli, rev)
- }
-}
-
-func TestVerifyHashKVAfterCompactionOnLastTombstone_MixVersions(t *testing.T) {
- e2e.BeforeTest(t)
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
-
- for _, keys := range [][]string{
- {"key0"},
- {"key0", "key1"},
- } {
- t.Run(fmt.Sprintf("#%v", keys), func(t *testing.T) {
- ctx := context.Background()
-
- cfg := e2e.NewConfigClientTLS()
- clus, err := e2e.NewEtcdProcessCluster(ctx, t,
- e2e.WithConfig(cfg),
- e2e.WithClusterSize(3),
- e2e.WithVersion(e2e.QuorumLastVersion))
- require.NoError(t, err)
- t.Cleanup(func() { clus.Close() })
-
- tombstoneRevs, lastestRev := populateDataForHashKV(t, clus, cfg.Client, keys)
-
- cli, err := e2e.NewEtcdctl(cfg.Client, clus.EndpointsGRPC())
- require.NoError(t, err)
-
- compactOnRev := tombstoneRevs[len(tombstoneRevs)-1]
- t.Logf("COMPACT rev=%d", compactOnRev)
- _, err = cli.Compact(ctx, compactOnRev, config.CompactOption{Physical: true})
- require.NoError(t, err)
-
- for rev := compactOnRev; rev <= lastestRev; rev++ {
- verifyConsistentHashKVAcrossAllMembers(t, cli, rev)
- }
- })
- }
-}
-
-// populateDataForHashKV populates some sample data, and return a slice of tombstone
-// revisions and the latest revision
-func populateDataForHashKV(t *testing.T, clus *e2e.EtcdProcessCluster, clientCfg e2e.ClientConfig, keys []string) ([]int64, int64) {
- c := newClient(t, clus.EndpointsGRPC(), clientCfg)
- defer c.Close()
-
- ctx := context.Background()
- totalOperations := 40
-
- var (
- tombStoneRevs []int64
- latestRev int64
- )
-
- deleteStep := 10 // submit a delete operation on every 10 operations
- for i := 1; i <= totalOperations; i++ {
- if i%deleteStep == 0 {
- t.Logf("Deleting key=%s", keys[0]) // Only delete the first key for simplicity
- resp, derr := c.Delete(ctx, keys[0])
- require.NoError(t, derr)
- latestRev = resp.Header.Revision
- tombStoneRevs = append(tombStoneRevs, resp.Header.Revision)
- continue
- }
-
- value := fmt.Sprintf("%d", i)
- var ops []clientv3.Op
- for _, key := range keys {
- ops = append(ops, clientv3.OpPut(key, value))
- }
-
- t.Logf("Writing keys: %v, value: %s", keys, value)
- resp, terr := c.Txn(ctx).Then(ops...).Commit()
- require.NoError(t, terr)
- require.True(t, resp.Succeeded)
- require.Len(t, resp.Responses, len(ops))
- latestRev = resp.Header.Revision
- }
- return tombStoneRevs, latestRev
-}
-
-func verifyConsistentHashKVAcrossAllMembers(t *testing.T, cli *e2e.EtcdctlV3, hashKVOnRev int64) {
- ctx := context.Background()
-
- t.Logf("HashKV on rev=%d", hashKVOnRev)
- resp, err := cli.HashKV(ctx, hashKVOnRev)
- require.NoError(t, err)
-
- require.Greater(t, len(resp), 1)
- require.NotEqual(t, 0, resp[0].Hash)
- t.Logf("One Hash value is %d", resp[0].Hash)
-
- for i := 1; i < len(resp); i++ {
- require.Equal(t, resp[0].Hash, resp[i].Hash)
- }
-}
diff --git a/tests/e2e/http_health_check_test.go b/tests/e2e/http_health_check_test.go
index 86b41bfd6544..7d770e31f084 100644
--- a/tests/e2e/http_health_check_test.go
+++ b/tests/e2e/http_health_check_test.go
@@ -150,7 +150,7 @@ func TestHTTPHealthHandler(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 20*time.Second)
defer cancel()
clus, err := e2e.NewEtcdProcessCluster(ctx, t, tc.clusterOptions...)
require.NoError(t, err)
@@ -313,7 +313,7 @@ func TestHTTPLivezReadyzHandler(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 20*time.Second)
defer cancel()
clus, err := e2e.NewEtcdProcessCluster(ctx, t, tc.clusterOptions...)
require.NoError(t, err)
@@ -336,7 +336,7 @@ func TestHTTPLivezReadyzHandler(t *testing.T) {
}
func doHealthCheckAndVerify(t *testing.T, client *http.Client, url string, expectTimeoutError bool, expectStatusCode int, expectRespSubStrings []string) {
- ctx, cancel := context.WithTimeout(context.Background(), healthCheckTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), healthCheckTimeout)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
require.NoErrorf(t, err, "failed to creat request %+v", err)
resp, herr := client.Do(req)
@@ -364,7 +364,7 @@ func triggerNoSpaceAlarm(ctx context.Context, t *testing.T, clus *e2e.EtcdProces
buf := strings.Repeat("b", os.Getpagesize())
etcdctl := clus.Etcdctl()
for {
- if err := etcdctl.Put(ctx, "foo", buf, config.PutOptions{}); err != nil {
+ if _, err := etcdctl.Put(ctx, "foo", buf, config.PutOptions{}); err != nil {
require.ErrorContains(t, err, "etcdserver: mvcc: database space exceeded")
break
}
@@ -375,7 +375,8 @@ func triggerSlowApply(ctx context.Context, t *testing.T, clus *e2e.EtcdProcessCl
// the following proposal will be blocked at applying stage
// because when apply index < committed index, linearizable read would time out.
require.NoError(t, clus.Procs[0].Failpoints().SetupHTTP(ctx, "beforeApplyOneEntryNormal", fmt.Sprintf(`sleep("%s")`, duration)))
- require.NoError(t, clus.Procs[1].Etcdctl().Put(ctx, "foo", "bar", config.PutOptions{}))
+ _, err := clus.Procs[1].Etcdctl().Put(ctx, "foo", "bar", config.PutOptions{})
+ require.NoError(t, err)
}
func blackhole(_ context.Context, t *testing.T, clus *e2e.EtcdProcessCluster, _ time.Duration) {
@@ -406,7 +407,7 @@ func triggerSlowBufferWriteBackWithAuth(ctx context.Context, t *testing.T, clus
func triggerCorrupt(ctx context.Context, t *testing.T, clus *e2e.EtcdProcessCluster, _ time.Duration) {
etcdctl := clus.Procs[0].Etcdctl()
for i := 0; i < 10; i++ {
- err := etcdctl.Put(ctx, "foo", "bar", config.PutOptions{})
+ _, err := etcdctl.Put(ctx, "foo", "bar", config.PutOptions{})
require.NoError(t, err)
}
err := clus.Procs[0].Kill()
diff --git a/tests/e2e/leader_snapshot_no_proxy_test.go b/tests/e2e/leader_snapshot_no_proxy_test.go
index 7b3c39270f39..1f4ab1fc4b41 100644
--- a/tests/e2e/leader_snapshot_no_proxy_test.go
+++ b/tests/e2e/leader_snapshot_no_proxy_test.go
@@ -35,7 +35,7 @@ import (
func TestRecoverSnapshotBackend(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
@@ -93,6 +93,6 @@ func TestRecoverSnapshotBackend(t *testing.T) {
require.NoError(t, err)
_, err = blackholedMember.Logs().ExpectWithContext(ctx, expect.ExpectedResponse{Value: "Recovering from snapshot backend"})
require.NoError(t, err)
- err = blackholedMember.Etcdctl().Put(ctx, "a", "1", config.PutOptions{})
+ _, err = blackholedMember.Etcdctl().Put(ctx, "a", "1", config.PutOptions{})
assert.NoError(t, err)
}
diff --git a/tests/e2e/logging_test.go b/tests/e2e/logging_test.go
index 9bcac2390b3b..3cea7ee0c68a 100644
--- a/tests/e2e/logging_test.go
+++ b/tests/e2e/logging_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"encoding/json"
"testing"
"time"
@@ -109,7 +108,7 @@ func TestNoErrorLogsDuringNormalOperations(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
e2e.BeforeTest(t)
- ctx := context.TODO()
+ ctx := t.Context()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, tc.options...)
require.NoError(t, err)
diff --git a/tests/e2e/main_test.go b/tests/e2e/main_test.go
index 58d7efb95da6..7ad5f207e50b 100644
--- a/tests/e2e/main_test.go
+++ b/tests/e2e/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 e2e
diff --git a/tests/e2e/member_no_proxy_test.go b/tests/e2e/member_no_proxy_test.go
index a52824ce9101..519037d371cf 100644
--- a/tests/e2e/member_no_proxy_test.go
+++ b/tests/e2e/member_no_proxy_test.go
@@ -17,7 +17,6 @@
package e2e
import (
- "context"
"strings"
"testing"
"time"
@@ -32,8 +31,7 @@ import (
// Refer to https://github.com/etcd-io/etcd/issues/20340#issuecomment-3105037914.
func TestReproduce20340(t *testing.T) {
e2e.BeforeTest(t)
-
- ctx := context.Background()
+ ctx := t.Context()
epc, members := mustCreateNewClusterByPromotingMembers(t, e2e.CurrentVersion, 3)
defer func() {
diff --git a/tests/e2e/metrics_test.go b/tests/e2e/metrics_test.go
index c76de746df6c..2b1cbdab7a2e 100644
--- a/tests/e2e/metrics_test.go
+++ b/tests/e2e/metrics_test.go
@@ -15,17 +15,12 @@
package e2e
import (
- "bytes"
"context"
"fmt"
- "io"
- "net/http"
"net/url"
"testing"
"time"
- dto "github.com/prometheus/client_model/go"
- "github.com/prometheus/common/expfmt"
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/version"
@@ -116,7 +111,8 @@ func learnerMetricApplyFromSnapshotTest(cx ctlCtx) {
func triggerSnapshot(ctx context.Context, cx ctlCtx) {
etcdctl := cx.epc.Procs[0].Etcdctl()
for i := 0; i < int(cx.epc.Cfg.ServerConfig.SnapshotCount); i++ {
- require.NoError(cx.t, etcdctl.Put(ctx, "k", "v", config.PutOptions{}))
+ _, err := etcdctl.Put(ctx, "k", "v", config.PutOptions{})
+ require.NoError(cx.t, err)
}
}
@@ -164,12 +160,15 @@ func TestNoMetricsMissing(t *testing.T) {
"etcd_debugging_mvcc_watch_stream_total",
"etcd_debugging_mvcc_watcher_total",
"etcd_debugging_server_lease_expired_total",
+ "etcd_debugging_server_watch_send_loop_watch_stream_duration_seconds",
+ "etcd_debugging_server_watch_send_loop_watch_stream_duration_per_event_seconds",
+ "etcd_debugging_server_watch_send_loop_control_stream_duration_seconds",
+ "etcd_debugging_server_watch_send_loop_progress_duration_seconds",
"etcd_debugging_snap_save_marshalling_duration_seconds",
"etcd_debugging_snap_save_total_duration_seconds",
"etcd_debugging_store_expires_total",
"etcd_debugging_store_watch_requests_total",
"etcd_debugging_store_watchers",
- "etcd_debugging_store_writes_total",
"etcd_disk_backend_commit_duration_seconds",
"etcd_disk_backend_defrag_duration_seconds",
"etcd_disk_backend_snapshot_duration_seconds",
@@ -213,6 +212,7 @@ func TestNoMetricsMissing(t *testing.T) {
"etcd_server_quota_backend_bytes",
"etcd_server_range_duration_seconds",
"etcd_server_read_indexes_failed_total",
+ "etcd_server_request_duration_seconds",
"etcd_server_slow_apply_total",
"etcd_server_slow_read_indexes_total",
"etcd_server_snapshot_apply_in_progress_total",
@@ -306,7 +306,7 @@ func TestNoMetricsMissing(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, tc.options...)
@@ -315,7 +315,7 @@ func TestNoMetricsMissing(t *testing.T) {
c := epc.Procs[0].Etcdctl()
for i := 0; i < 3; i++ {
- err = c.Put(ctx, fmt.Sprintf("key_%d", i), fmt.Sprintf("value_%d", i), config.PutOptions{})
+ _, err = c.Put(ctx, fmt.Sprintf("key_%d", i), fmt.Sprintf("value_%d", i), config.PutOptions{})
require.NoError(t, err)
}
_, err = c.Get(ctx, "k", config.GetOptions{})
@@ -324,7 +324,7 @@ func TestNoMetricsMissing(t *testing.T) {
metricsURL, err := url.JoinPath(epc.Procs[0].Config().ClientURL, "metrics")
require.NoError(t, err)
- mfs, err := getMetrics(metricsURL)
+ mfs, err := e2e.GetMetrics(metricsURL)
require.NoError(t, err)
var missingMetrics []string
@@ -341,23 +341,6 @@ func TestNoMetricsMissing(t *testing.T) {
}
}
-func getMetrics(metricsURL string) (map[string]*dto.MetricFamily, error) {
- httpClient := http.Client{Transport: &http.Transport{}}
- resp, err := httpClient.Get(metricsURL)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- data, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
-
- var parser expfmt.TextParser
- return parser.TextToMetricFamilies(bytes.NewReader(data))
-}
-
// formatMetrics is only for test purpose
/*func formatMetrics(metrics []string) string {
quoted := make([]string, len(metrics))
diff --git a/tests/e2e/promote_experimental_flag_test.go b/tests/e2e/promote_experimental_flag_test.go
index 13a8fcba4fb1..0ebc0d4491bc 100644
--- a/tests/e2e/promote_experimental_flag_test.go
+++ b/tests/e2e/promote_experimental_flag_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"testing"
"time"
@@ -28,7 +27,7 @@ import (
func TestWarningApplyDuration(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(1),
e2e.WithWarningUnaryRequestDuration(time.Microsecond),
)
@@ -42,49 +41,9 @@ func TestWarningApplyDuration(t *testing.T) {
})
cc := epc.Etcdctl()
- err = cc.Put(context.TODO(), "foo", "bar", config.PutOptions{})
+ _, err = cc.Put(t.Context(), "foo", "bar", config.PutOptions{})
require.NoErrorf(t, err, "error on put")
// verify warning
e2e.AssertProcessLogs(t, epc.Procs[0], "request stats")
}
-
-// TestExperimentalWarningApplyDuration tests the experimental warning apply duration
-// TODO: this test is a duplicate of TestWarningApplyDuration except it uses --experimental-warning-unary-request-duration
-// Remove this test after --experimental-warning-unary-request-duration flag is removed.
-func TestExperimentalWarningApplyDuration(t *testing.T) {
- e2e.BeforeTest(t)
-
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
- e2e.WithClusterSize(1),
- e2e.WithExperimentalWarningUnaryRequestDuration(time.Microsecond),
- )
- if err != nil {
- t.Fatalf("could not start etcd process cluster (%v)", err)
- }
- t.Cleanup(func() {
- if errC := epc.Close(); errC != nil {
- t.Fatalf("error closing etcd processes (%v)", errC)
- }
- })
-
- cc := epc.Etcdctl()
- err = cc.Put(context.TODO(), "foo", "bar", config.PutOptions{})
- require.NoErrorf(t, err, "error on put")
-
- // verify warning
- e2e.AssertProcessLogs(t, epc.Procs[0], "request stats")
-}
-
-func TestBothWarningApplyDurationFlagsFail(t *testing.T) {
- e2e.BeforeTest(t)
-
- _, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
- e2e.WithClusterSize(1),
- e2e.WithWarningUnaryRequestDuration(time.Second),
- e2e.WithExperimentalWarningUnaryRequestDuration(time.Second),
- )
- if err == nil {
- t.Fatal("Expected process to fail")
- }
-}
diff --git a/tests/e2e/reproduce_17780_test.go b/tests/e2e/reproduce_17780_test.go
index f5ef97b91a4b..fc54b8e8cc9a 100644
--- a/tests/e2e/reproduce_17780_test.go
+++ b/tests/e2e/reproduce_17780_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"fmt"
"testing"
"time"
@@ -34,7 +33,7 @@ func TestReproduce17780(t *testing.T) {
compactionBatchLimit := 10
- ctx := context.TODO()
+ ctx := t.Context()
clus, cerr := e2e.NewEtcdProcessCluster(ctx, t,
e2e.WithClusterSize(3),
e2e.WithGoFailEnabled(true),
diff --git a/tests/e2e/reproduce_19406_test.go b/tests/e2e/reproduce_19406_test.go
new file mode 100644
index 000000000000..2fce10e56b1b
--- /dev/null
+++ b/tests/e2e/reproduce_19406_test.go
@@ -0,0 +1,112 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 e2e
+
+import (
+ "fmt"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/pkg/v3/stringutil"
+ "go.etcd.io/etcd/tests/v3/framework/e2e"
+)
+
+// TestReproduce19406 reproduces the issue: https://github.com/etcd-io/etcd/issues/19406
+func TestReproduce19406(t *testing.T) {
+ e2e.BeforeTest(t)
+
+ compactionSleepInterval := 100 * time.Millisecond
+ ctx := t.Context()
+
+ clus, cerr := e2e.NewEtcdProcessCluster(ctx, t,
+ e2e.WithClusterSize(1),
+ e2e.WithGoFailEnabled(true),
+ e2e.WithCompactionBatchLimit(1),
+ e2e.WithCompactionSleepInterval(compactionSleepInterval),
+ )
+ require.NoError(t, cerr)
+ t.Cleanup(func() { require.NoError(t, clus.Stop()) })
+
+ // Produce some data
+ cli := newClient(t, clus.EndpointsGRPC(), e2e.ClientConfig{})
+ valueSize := 10
+ var latestRevision int64
+
+ produceKeyNum := 20
+ for i := 0; i <= produceKeyNum; i++ {
+ resp, err := cli.Put(ctx, fmt.Sprintf("%d", i), stringutil.RandString(uint(valueSize)))
+ require.NoError(t, err)
+ latestRevision = resp.Header.Revision
+ }
+
+ // Sleep for PerCompactionInterationInterval to simulate a single iteration of compaction lasting at least this duration.
+ PerCompactionInterationInterval := compactionSleepInterval
+ require.NoError(t, clus.Procs[0].Failpoints().SetupHTTP(ctx, "compactAfterAcquiredBatchTxLock",
+ fmt.Sprintf(`sleep("%s")`, PerCompactionInterationInterval)))
+
+ // start compaction
+ t.Log("start compaction...")
+ _, err := cli.Compact(ctx, latestRevision, clientv3.WithCompactPhysical())
+ require.NoError(t, err)
+ t.Log("finished compaction...")
+
+ // Validate that total compaction sleep interval
+ // Compaction runs in batches. During each batch, it acquires a lock, releases it at the end,
+ // and then waits for a compactionSleepInterval before starting the next batch. This pause
+ // allows PUT requests to be processed.
+ // Therefore, the total compaction sleep interval larger or equal to
+ // (compaction iteration number - 1) * compactionSleepInterval
+ httpEndpoint := clus.EndpointsHTTP()[0]
+ totalKeys := produceKeyNum + 1
+ pauseDuration, totalDuration := getEtcdCompactionMetrics(t, httpEndpoint)
+ require.NoError(t, err)
+ actualSleepInterval := time.Duration(totalDuration-pauseDuration) * time.Millisecond
+ expectSleepInterval := compactionSleepInterval * time.Duration(totalKeys)
+ t.Logf("db_compaction_pause_duration: %.2f db_compaction_total_duration: %.2f, totalKeys: %d",
+ pauseDuration, totalDuration, totalKeys)
+ require.GreaterOrEqualf(t, actualSleepInterval, expectSleepInterval,
+ "expect total compact sleep interval larger than (%v) but got (%v)",
+ expectSleepInterval, actualSleepInterval)
+}
+
+func getEtcdCompactionMetrics(t *testing.T, httpEndpoint string) (pauseDuration, totalDuration float64) {
+ metricsURL, err := url.JoinPath(httpEndpoint, "metrics")
+ require.NoError(t, err)
+
+ // Fetch metrics from the endpoint
+ metricFamilies, err := e2e.GetMetrics(metricsURL)
+ require.NoError(t, err)
+
+ // Extract sum from histogram metric
+ getHistogramSum := func(name string) float64 {
+ mf, ok := metricFamilies[name]
+ require.Truef(t, ok, "metric %q not found", name)
+ require.NotEmptyf(t, mf.Metric, "metric %q has no data", name)
+
+ hist := mf.Metric[0].GetHistogram()
+ require.NotEmptyf(t, hist, "metric %q is not a histogram", name)
+
+ return hist.GetSampleSum()
+ }
+
+ pauseDuration = getHistogramSum("etcd_debugging_mvcc_db_compaction_pause_duration_milliseconds")
+ totalDuration = getHistogramSum("etcd_debugging_mvcc_db_compaction_total_duration_milliseconds")
+
+ return pauseDuration, totalDuration
+}
diff --git a/tests/e2e/reproduce_20271_test.go b/tests/e2e/reproduce_20271_test.go
index 86275b6a2580..4dbbb3939951 100644
--- a/tests/e2e/reproduce_20271_test.go
+++ b/tests/e2e/reproduce_20271_test.go
@@ -54,10 +54,11 @@ func TestIssue20271(t *testing.T) {
t.Log("Step 1: Write some data to the cluster")
for i := 0; i < snapCount*5; i++ {
- require.NoError(t, epc.Procs[0].Etcdctl().Put(ctx,
+ _, err = epc.Procs[0].Etcdctl().Put(ctx,
fmt.Sprintf("foo%d", i),
strings.Repeat("Oops", 1024),
- config.PutOptions{}))
+ config.PutOptions{})
+ require.NoError(t, err)
}
t.Log(`Step 2: Config the third member to sleep 15s after OpenSnapshotBackend and use SIGSTOP to pause it.`)
@@ -70,7 +71,7 @@ func TestIssue20271(t *testing.T) {
require.NoError(t, err)
}
- t.Log("Step 4: Restarting the first two members to change term and trigger new leader to send snapshot file to the third member.")
+ t.Log("Step 4: Restarting the first two members to re-connect to the paused member, so the inflight messages will be dropped. This will trigger new leader to send snapshot file to the third member.")
for _, proc := range epc.Procs[:2] {
require.NoError(t, proc.Restart(ctx))
}
@@ -86,11 +87,11 @@ to override boltdb file. So, for the following changes, the third member will co
t.Log("Step 6: Write some data to the cluster")
for i := 0; i < snapCount/2; i++ {
- err = epc.Procs[0].Etcdctl().Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("Oops", 1), config.PutOptions{})
+ _, err = epc.Procs[0].Etcdctl().Put(ctx, fmt.Sprintf("foo%d", i), strings.Repeat("Oops", 1), config.PutOptions{})
require.NoError(t, err)
}
- t.Log("Step 7: Restart the third member. It recovers from the new boltdb file. Therefore, data writen in Step 6 is lost.")
+ t.Log("Step 7: Restart the third member. It recovers from the new boltdb file. Therefore, data written in Step 6 is lost.")
require.NoError(t, epc.Procs[2].Restart(ctx))
t.Log("Step 8: Check hashkv of each member")
diff --git a/tests/e2e/runtime_reconfiguration_test.go b/tests/e2e/runtime_reconfiguration_test.go
index 1736126e6a6e..650e9358d2ba 100644
--- a/tests/e2e/runtime_reconfiguration_test.go
+++ b/tests/e2e/runtime_reconfiguration_test.go
@@ -60,7 +60,7 @@ func TestRuntimeReconfigGrowClusterSize(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithClusterSize(tc.clusterSize))
@@ -102,7 +102,7 @@ func TestRuntimeReconfigDecreaseClusterSize(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithClusterSize(tc.clusterSize))
@@ -140,7 +140,7 @@ func TestRuntimeReconfigRollingUpgrade(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithClusterSize(3))
diff --git a/tests/e2e/txn_range_consistency_test.go b/tests/e2e/txn_range_consistency_test.go
new file mode 100644
index 000000000000..d17a28fa7531
--- /dev/null
+++ b/tests/e2e/txn_range_consistency_test.go
@@ -0,0 +1,75 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 e2e
+
+import (
+ "fmt"
+ "math/rand"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/framework/e2e"
+)
+
+func TestTxnRangeConsistency(t *testing.T) {
+ e2e.BeforeTest(t)
+
+ ctx := t.Context()
+ clus, cerr := e2e.NewEtcdProcessCluster(ctx, t,
+ e2e.WithClusterSize(3),
+ e2e.WithSnapshotCount(1000),
+ )
+ require.NoError(t, cerr)
+
+ t.Cleanup(func() { clus.Stop() })
+
+ targetIdx := rand.Intn(3)
+ cli := newClient(t, clus.Procs[targetIdx].EndpointsGRPC(), e2e.ClientConfig{})
+
+ t.Log("Put key-value [k1...k20]")
+ var revision int64
+ for i := 1; i <= 20; i++ {
+ resp, err := cli.Put(ctx, fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i))
+ require.NoError(t, err)
+ revision = resp.Header.Revision
+ }
+
+ t.Logf("Compact on the latest revision %d", revision)
+ _, err := cli.Compact(ctx, revision)
+ require.NoError(t, err)
+
+ t.Log("Send txn to make data inconsistent among etcdservers")
+ cmp := clientv3.Compare(clientv3.Value("k1"), "=", "v1")
+ then := []clientv3.Op{
+ clientv3.OpPut("k2", "foo"),
+ clientv3.OpGet("k1", clientv3.WithRev(revision-1)),
+ }
+ _, err = cli.Txn(ctx).If(cmp).Then(then...).Commit()
+ require.Error(t, err)
+ require.Contains(t, err.Error(), rpctypes.ErrCompacted.Error())
+
+ t.Log("Verify all members have consistent data on key 'k2'")
+ for i := 0; i < clus.Cfg.ClusterSize; i++ {
+ idx := (targetIdx + i) % clus.Cfg.ClusterSize
+ cli = newClient(t, clus.Procs[idx].EndpointsGRPC(), e2e.ClientConfig{})
+ resp, err := cli.Get(ctx, "k2")
+ require.NoError(t, err)
+ assert.Equal(t, "v2", string(resp.Kvs[0].Value))
+ }
+}
diff --git a/tests/e2e/utils.go b/tests/e2e/utils.go
index 8917bd8072ad..bff9b474fc8c 100644
--- a/tests/e2e/utils.go
+++ b/tests/e2e/utils.go
@@ -33,7 +33,6 @@ import (
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
- "google.golang.org/grpc"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
@@ -50,7 +49,6 @@ func newClient(t *testing.T, entpoints []string, cfg e2e.ClientConfig) *clientv3
ccfg := clientv3.Config{
Endpoints: entpoints,
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
if tlscfg != nil {
ccfg.TLS, err = tlscfg.ClientConfig()
@@ -69,13 +67,13 @@ func newClient(t *testing.T, entpoints []string, cfg e2e.ClientConfig) *clientv3
}
// tlsInfo follows the Client-to-server communication in https://etcd.io/docs/v3.6/op-guide/security/#basic-setup
-func tlsInfo(t testing.TB, cfg e2e.ClientConfig) (*transport.TLSInfo, error) {
+func tlsInfo(tb testing.TB, cfg e2e.ClientConfig) (*transport.TLSInfo, error) {
switch cfg.ConnectionType {
case e2e.ClientNonTLS, e2e.ClientTLSAndNonTLS:
return nil, nil
case e2e.ClientTLS:
if cfg.AutoTLS {
- tls, err := transport.SelfCert(zap.NewNop(), t.TempDir(), []string{"localhost"}, 1)
+ tls, err := transport.SelfCert(zap.NewNop(), tb.TempDir(), []string{"localhost"}, 1)
if err != nil {
return nil, fmt.Errorf("failed to generate cert: %w", err)
}
@@ -145,16 +143,6 @@ func getMemberIDByName(ctx context.Context, c *e2e.EtcdctlV3, name string) (id u
return 0, false, nil
}
-func patchArgs(args []string, flag, newValue string) error {
- for i, arg := range args {
- if strings.Contains(arg, flag) {
- args[i] = fmt.Sprintf("--%s=%s", flag, newValue)
- return nil
- }
- }
- return fmt.Errorf("--%s flag not found", flag)
-}
-
func generateCertsForIPs(tempDir string, ips []net.IP) (caFile string, certFiles []string, keyFiles []string, err error) {
ca := &x509.Certificate{
SerialNumber: big.NewInt(1001),
diff --git a/tests/e2e/utl_migrate_test.go b/tests/e2e/utl_migrate_test.go
index 5ee933f0ef28..6bdd48fdc5f0 100644
--- a/tests/e2e/utl_migrate_test.go
+++ b/tests/e2e/utl_migrate_test.go
@@ -17,7 +17,6 @@
package e2e
import (
- "context"
"fmt"
"path/filepath"
"strings"
@@ -55,78 +54,79 @@ func TestEtctlutlMigrate(t *testing.T) {
targetVersion: "abc",
clusterSize: 1,
expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "abc"`,
- expectStorageVersion: &version.V3_6,
+ expectStorageVersion: &version.V3_7,
},
{
name: "Invalid target version",
targetVersion: "3.a",
clusterSize: 1,
expectLogsSubString: `Error: failed to parse target version: strconv.ParseInt: parsing "a": invalid syntax`,
- expectStorageVersion: &version.V3_6,
+ expectStorageVersion: &version.V3_7,
},
{
name: "Target with only major version is invalid",
targetVersion: "3",
clusterSize: 1,
expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "3"`,
- expectStorageVersion: &version.V3_6,
+ expectStorageVersion: &version.V3_7,
},
{
name: "Target with patch version is invalid",
targetVersion: "3.6.0",
clusterSize: 1,
expectLogsSubString: `Error: wrong target version format, expected "X.Y", got "3.6.0"`,
- expectStorageVersion: &version.V3_6,
+ expectStorageVersion: &version.V3_7,
},
{
- name: "Migrate v3.5 to v3.5 is no-op",
- clusterVersion: e2e.LastVersion,
- clusterSize: 1,
- targetVersion: "3.5",
- expectLogsSubString: "storage version up-to-date\t" + `{"storage-version": "3.5"}`,
+ name: "Migrate v3.6 to v3.6 is no-op",
+ clusterVersion: e2e.LastVersion,
+ clusterSize: 1,
+ targetVersion: "3.6",
+ expectStorageVersion: &version.V3_6,
+ expectLogsSubString: "storage version up-to-date\t" + `{"storage-version": "3.6"}`,
},
{
name: "Upgrade 1 member cluster from v3.5 to v3.6 should work",
clusterVersion: e2e.LastVersion,
clusterSize: 1,
- targetVersion: "3.6",
- expectStorageVersion: &version.V3_6,
+ targetVersion: "3.7",
+ expectStorageVersion: &version.V3_7,
},
{
- name: "Upgrade 3 member cluster from v3.5 to v3.6 should work",
+ name: "Upgrade 3 member cluster from v3.6 to v3.7 should work",
clusterVersion: e2e.LastVersion,
clusterSize: 3,
- targetVersion: "3.6",
- expectStorageVersion: &version.V3_6,
+ targetVersion: "3.7",
+ expectStorageVersion: &version.V3_7,
},
{
- name: "Migrate v3.6 to v3.6 is no-op",
- targetVersion: "3.6",
+ name: "Migrate v3.7 to v3.7 is no-op",
+ targetVersion: "3.7",
clusterSize: 1,
- expectLogsSubString: "storage version up-to-date\t" + `{"storage-version": "3.6"}`,
- expectStorageVersion: &version.V3_6,
+ expectLogsSubString: "storage version up-to-date\t" + `{"storage-version": "3.7"}`,
+ expectStorageVersion: &version.V3_7,
},
{
- name: "Downgrade 1 member cluster from v3.6 to v3.5 should work",
- targetVersion: "3.5",
+ name: "Downgrade 1 member cluster from v3.7 to v3.6 should work",
+ targetVersion: "3.6",
clusterSize: 1,
expectLogsSubString: "updated storage version",
- expectStorageVersion: nil, // 3.5 doesn't have the field `storageVersion`, so it returns nil.
+ expectStorageVersion: &version.V3_6,
},
{
- name: "Downgrade 3 member cluster from v3.6 to v3.5 should work",
- targetVersion: "3.5",
+ name: "Downgrade 3 member cluster from v3.7 to v3.6 should work",
+ targetVersion: "3.6",
clusterSize: 3,
expectLogsSubString: "updated storage version",
- expectStorageVersion: nil, // 3.5 doesn't have the field `storageVersion`, so it returns nil.
+ expectStorageVersion: &version.V3_6,
},
{
- name: "Upgrade v3.6 to v3.7 with force should work",
- targetVersion: "3.7",
+ name: "Upgrade v3.7 to v3.8 with force should work",
+ targetVersion: "3.8",
clusterSize: 1,
force: true,
- expectLogsSubString: "forcefully set storage version\t" + `{"storage-version": "3.7"}`,
- expectStorageVersion: &semver.Version{Major: 3, Minor: 7},
+ expectLogsSubString: "forcefully set storage version\t" + `{"storage-version": "3.8"}`,
+ expectStorageVersion: &semver.Version{Major: 3, Minor: 8},
},
}
for _, tc := range tcs {
@@ -138,7 +138,7 @@ func TestEtctlutlMigrate(t *testing.T) {
}
dataDirPath := t.TempDir()
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithVersion(tc.clusterVersion),
e2e.WithDataDirPath(dataDirPath),
e2e.WithClusterSize(1),
@@ -163,7 +163,7 @@ func TestEtctlutlMigrate(t *testing.T) {
require.NoError(t, e2e.SpawnWithExpect(append(prefixArgs, "put", fmt.Sprintf("%d", i), "value"), expect.ExpectedResponse{Value: "OK"}))
}
- t.Log("Stopping the the members")
+ t.Log("Stopping the members")
for i := 0; i < len(epc.Procs); i++ {
t.Logf("Stopping server %d: %v", i, epc.Procs[i].EndpointsGRPC())
err = epc.Procs[i].Stop()
diff --git a/tests/e2e/v2store_deprecation_test.go b/tests/e2e/v2store_deprecation_test.go
index b267ae240b8a..ccec2082832c 100644
--- a/tests/e2e/v2store_deprecation_test.go
+++ b/tests/e2e/v2store_deprecation_test.go
@@ -15,22 +15,19 @@
package e2e
import (
- "bytes"
"context"
"fmt"
- "reflect"
"sort"
"strings"
"testing"
- "github.com/coreos/go-semver/semver"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
- "go.etcd.io/etcd/pkg/v3/expect"
+ "go.etcd.io/etcd/client/pkg/v3/types"
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
@@ -39,17 +36,6 @@ import (
"go.etcd.io/etcd/tests/v3/framework/e2e"
)
-func writeCustomV2Data(t testing.TB, epc *e2e.EtcdProcessCluster, count int) {
- for i := 0; i < count; i++ {
- if err := e2e.CURLPut(epc, e2e.CURLReq{
- Endpoint: "/v2/keys/foo", Value: "bar" + fmt.Sprint(i),
- Expected: expect.ExpectedResponse{Value: `{"action":"set","node":{"key":"/foo","value":"bar` + fmt.Sprint(i)},
- }); err != nil {
- t.Fatalf("failed put with curl (%v)", err)
- }
- }
-}
-
func TestV2DeprecationNotYet(t *testing.T) {
e2e.BeforeTest(t)
t.Log("Verify its infeasible to start etcd with --v2-deprecation=not-yet mode")
@@ -60,103 +46,69 @@ func TestV2DeprecationNotYet(t *testing.T) {
assert.NoError(t, err)
}
-func TestV2DeprecationWriteOnlyWAL(t *testing.T) {
- e2e.BeforeTest(t)
- dataDirPath := t.TempDir()
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
- cfg := e2e.ConfigStandalone(*e2e.NewConfig(
- e2e.WithVersion(e2e.LastVersion),
- e2e.WithEnableV2(true),
- e2e.WithDataDirPath(dataDirPath),
- ))
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
- require.NoError(t, err)
- memberDataDir := epc.Procs[0].Config().DataDirPath
-
- writeCustomV2Data(t, epc, 1)
-
- require.NoError(t, epc.Stop())
-
- t.Log("Verify its infeasible to start etcd with --v2-deprecation=write-only mode")
- proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--v2-deprecation=write-only", "--data-dir=" + memberDataDir}, nil)
- require.NoError(t, err)
-
- _, err = proc.Expect("detected disallowed v2 WAL for stage --v2-deprecation=write-only")
- assert.NoError(t, err)
-}
-
-func TestV2DeprecationWriteOnlySnapshot(t *testing.T) {
- e2e.BeforeTest(t)
- dataDirPath := t.TempDir()
-
- if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
- t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
- }
- cfg := e2e.ConfigStandalone(*e2e.NewConfig(
- e2e.WithVersion(e2e.LastVersion),
- e2e.WithEnableV2(true),
- e2e.WithDataDirPath(dataDirPath),
- e2e.WithSnapshotCount(10),
- ))
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
- require.NoError(t, err)
- memberDataDir := epc.Procs[0].Config().DataDirPath
-
- // We need to exceed 'SnapshotCount' such that v2 snapshot is dumped.
- writeCustomV2Data(t, epc, 10)
-
- require.NoError(t, epc.Stop())
-
- t.Log("Verify its infeasible to start etcd with --v2-deprecation=write-only mode")
- proc, err := e2e.SpawnCmd([]string{e2e.BinPath.Etcd, "--v2-deprecation=write-only", "--data-dir=" + memberDataDir}, nil)
- require.NoError(t, err)
- defer proc.Close()
-
- _, err = proc.Expect("detected disallowed custom content in v2store for stage --v2-deprecation=write-only")
- assert.NoError(t, err)
-}
-
+// TestV2DeprecationSnapshotMatches ensures that etcd v3.7 still commits v2 store
+// changes to the snapshot for backwards compatibility.
func TestV2DeprecationSnapshotMatches(t *testing.T) {
e2e.BeforeTest(t)
lastReleaseData := t.TempDir()
currentReleaseData := t.TempDir()
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
t.Skipf("%q does not exist", e2e.BinPath.EtcdLastRelease)
}
+ ctx, cancel := context.WithCancel(t.Context())
+ defer cancel()
+
var snapshotCount uint64 = 10
+
epc := runEtcdAndCreateSnapshot(t, e2e.LastVersion, lastReleaseData, snapshotCount)
oldMemberDataDir := epc.Procs[0].Config().DataDirPath
cc1 := epc.Etcdctl()
- members1 := addAndRemoveKeysAndMembers(ctx, t, cc1, snapshotCount)
+ addAndRemoveKeysAndMembers(ctx, t, cc1, snapshotCount)
require.NoError(t, epc.Close())
+
epc = runEtcdAndCreateSnapshot(t, e2e.CurrentVersion, currentReleaseData, snapshotCount)
newMemberDataDir := epc.Procs[0].Config().DataDirPath
cc2 := epc.Etcdctl()
- members2 := addAndRemoveKeysAndMembers(ctx, t, cc2, snapshotCount)
+ addAndRemoveKeysAndMembers(ctx, t, cc2, snapshotCount)
require.NoError(t, epc.Close())
- assertSnapshotsMatch(t, oldMemberDataDir, newMemberDataDir, func(data []byte) []byte {
- // Patch members ids
- for i, mid := range members1 {
- data = bytes.Replace(data, []byte(fmt.Sprintf("%x", mid)), []byte(fmt.Sprintf("%d", i+1)), -1)
- }
- for i, mid := range members2 {
- data = bytes.Replace(data, []byte(fmt.Sprintf("%x", mid)), []byte(fmt.Sprintf("%d", i+1)), -1)
- }
- return data
- })
+ assertSnapshotsMatch(t, oldMemberDataDir, newMemberDataDir)
+}
+
+func addAndRemoveKeysAndMembers(ctx context.Context, tb testing.TB, cc *e2e.EtcdctlV3, snapshotCount uint64) {
+ // Execute some non-trivial key&member operation
+ var i uint64
+ for i = 0; i < snapshotCount*3; i++ {
+ _, err := cc.Put(ctx, fmt.Sprintf("%d", i), "1", config.PutOptions{})
+ require.NoError(tb, err)
+ }
+ member1, err := cc.MemberAddAsLearner(ctx, "member1", []string{"http://127.0.0.1:2000"})
+ require.NoError(tb, err)
+
+ for i = 0; i < snapshotCount*2; i++ {
+ _, err = cc.Delete(ctx, fmt.Sprintf("%d", i), config.DeleteOptions{})
+ require.NoError(tb, err)
+ }
+ _, err = cc.MemberRemove(ctx, member1.Member.ID)
+ require.NoError(tb, err)
+
+ for i = 0; i < snapshotCount; i++ {
+ _, err = cc.Put(ctx, fmt.Sprintf("%d", i), "2", config.PutOptions{})
+ require.NoError(tb, err)
+ }
+ _, err = cc.MemberAddAsLearner(ctx, "member2", []string{"http://127.0.0.1:2001"})
+ require.NoError(tb, err)
+
+ for i = 0; i < snapshotCount/2; i++ {
+ _, err = cc.Put(ctx, fmt.Sprintf("%d", i), "3", config.PutOptions{})
+ assert.NoError(tb, err)
+ }
}
func TestV2DeprecationSnapshotRecover(t *testing.T) {
e2e.BeforeTest(t)
dataDir := t.TempDir()
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
if !fileutil.Exist(e2e.BinPath.EtcdLastRelease) {
@@ -176,7 +128,7 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) {
e2e.WithVersion(e2e.CurrentVersion),
e2e.WithDataDirPath(dataDir),
))
- epc, err = e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
+ epc, err = e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
require.NoError(t, err)
cc = epc.Etcdctl()
@@ -191,93 +143,76 @@ func TestV2DeprecationSnapshotRecover(t *testing.T) {
assert.NoError(t, epc.Close())
}
-func runEtcdAndCreateSnapshot(t testing.TB, serverVersion e2e.ClusterVersion, dataDir string, snapshotCount uint64) *e2e.EtcdProcessCluster {
+func runEtcdAndCreateSnapshot(tb testing.TB, serverVersion e2e.ClusterVersion, dataDir string, snapshotCount uint64) *e2e.EtcdProcessCluster {
cfg := e2e.ConfigStandalone(*e2e.NewConfig(
e2e.WithVersion(serverVersion),
e2e.WithDataDirPath(dataDir),
e2e.WithSnapshotCount(snapshotCount),
e2e.WithKeepDataDir(true),
))
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(cfg))
- assert.NoError(t, err)
+ epc, err := e2e.NewEtcdProcessCluster(tb.Context(), tb, e2e.WithConfig(cfg))
+ assert.NoError(tb, err)
return epc
}
-func addAndRemoveKeysAndMembers(ctx context.Context, t testing.TB, cc *e2e.EtcdctlV3, snapshotCount uint64) (members []uint64) {
- // Execute some non-trivial key&member operation
- var i uint64
- for i = 0; i < snapshotCount*3; i++ {
- err := cc.Put(ctx, fmt.Sprintf("%d", i), "1", config.PutOptions{})
- require.NoError(t, err)
- }
- member1, err := cc.MemberAddAsLearner(ctx, "member1", []string{"http://127.0.0.1:2000"})
- require.NoError(t, err)
- members = append(members, member1.Member.ID)
-
- for i = 0; i < snapshotCount*2; i++ {
- _, err = cc.Delete(ctx, fmt.Sprintf("%d", i), config.DeleteOptions{})
- require.NoError(t, err)
- }
- _, err = cc.MemberRemove(ctx, member1.Member.ID)
- require.NoError(t, err)
-
- for i = 0; i < snapshotCount; i++ {
- err = cc.Put(ctx, fmt.Sprintf("%d", i), "2", config.PutOptions{})
- require.NoError(t, err)
- }
- member2, err := cc.MemberAddAsLearner(ctx, "member2", []string{"http://127.0.0.1:2001"})
- require.NoError(t, err)
- members = append(members, member2.Member.ID)
-
- for i = 0; i < snapshotCount/2; i++ {
- err = cc.Put(ctx, fmt.Sprintf("%d", i), "3", config.PutOptions{})
- assert.NoError(t, err)
- }
- return members
-}
-
func filterSnapshotFiles(path string) bool {
return strings.HasSuffix(path, ".snap")
}
-func assertSnapshotsMatch(t testing.TB, firstDataDir, secondDataDir string, patch func([]byte) []byte) {
- lg := zaptest.NewLogger(t)
+func assertSnapshotsMatch(tb testing.TB, firstDataDir, secondDataDir string) {
+ lg := zaptest.NewLogger(tb)
+
firstFiles, err := fileutil.ListFiles(firstDataDir, filterSnapshotFiles)
- require.NoError(t, err)
+ require.NoError(tb, err)
+
secondFiles, err := fileutil.ListFiles(secondDataDir, filterSnapshotFiles)
- require.NoError(t, err)
- assert.NotEmpty(t, firstFiles)
- assert.NotEmpty(t, secondFiles)
- assert.Equal(t, len(firstFiles), len(secondFiles))
+ require.NoError(tb, err)
+
+ assert.NotEmpty(tb, firstFiles)
+ assert.NotEmpty(tb, secondFiles)
+ assert.Len(tb, secondFiles, len(firstFiles))
+
sort.Strings(firstFiles)
sort.Strings(secondFiles)
for i := 0; i < len(firstFiles); i++ {
- firstSnapshot, err := snap.Read(lg, firstFiles[i])
- require.NoError(t, err)
- secondSnapshot, err := snap.Read(lg, secondFiles[i])
- require.NoError(t, err)
- assertMembershipEqual(t, openSnap(patch(firstSnapshot.Data)), openSnap(patch(secondSnapshot.Data)))
+ assertV2StoreMembershipEqual(tb, lg, firstFiles[i], secondFiles[i])
}
}
-func assertMembershipEqual(t testing.TB, firstStore v2store.Store, secondStore v2store.Store) {
- rc1 := membership.NewCluster(zaptest.NewLogger(t))
- rc1.SetStore(firstStore)
- rc1.Recover(func(lg *zap.Logger, v *semver.Version) {})
+func assertV2StoreMembershipEqual(tb testing.TB, lg *zap.Logger, firstSnapPath, secondSnapPath string) {
+ st1 := loadV2StoreData(tb, lg, firstSnapPath)
+ st2 := loadV2StoreData(tb, lg, secondSnapPath)
- rc2 := membership.NewCluster(zaptest.NewLogger(t))
- rc2.SetStore(secondStore)
- rc2.Recover(func(lg *zap.Logger, v *semver.Version) {})
+ st1Members, st1Deleted := membership.MembersFromStore(lg, st1)
+ st2Members, st2Deleted := membership.MembersFromStore(lg, st2)
- // membership should match
- if !reflect.DeepEqual(rc1.Members(), rc2.Members()) {
- t.Logf("memberids_from_last_version = %+v, member_ids_from_current_version = %+v", rc1.MemberIDs(), rc2.MemberIDs())
- t.Errorf("members_from_last_version_snapshot = %+v, members_from_current_version_snapshot %+v", rc1.Members(), rc2.Members())
- }
+ require.Lenf(tb, st1Members, len(st2Members), "number of members in v2 store do not match")
+ require.NotEmptyf(tb, st1Members, "no members found in v2 store")
+ require.Lenf(tb, st1Deleted, len(st2Deleted), "number of deleted members in v2 store do not match")
+
+ // remove ID because original ID was generated from hash of peerURLs + clusterName + time
+ require.Equal(tb, rebuildMembers(tb, st1Members), rebuildMembers(tb, st2Members))
}
-func openSnap(data []byte) v2store.Store {
- st := v2store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
- st.Recovery(data)
- return st
+// loadV2StoreData reads v2 store from the snapshot file at fpath.
+func loadV2StoreData(tb testing.TB, lg *zap.Logger, fpath string) v2store.Store {
+ sn, err := snap.Read(lg, fpath)
+ require.NoError(tb, err)
+
+ v2data := v2store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix)
+ v2data.Recovery(sn.Data)
+ return v2data
+}
+
+// rebuildMembers rebuilds the members map with zeroed IDs and peerURLs as keys.
+func rebuildMembers(tb testing.TB, members map[types.ID]*membership.Member) map[string]*membership.Member {
+ newMembers := make(map[string]*membership.Member)
+ for _, m := range members {
+ peerURLs, err := types.NewURLs(m.PeerURLs)
+ require.NoError(tb, err)
+
+ m.ID = 0
+ newMembers[peerURLs.String()] = m
+ }
+ return newMembers
}
diff --git a/tests/e2e/v3_curl_auth_test.go b/tests/e2e/v3_curl_auth_test.go
index c8745d23179a..8a07b67dd3f8 100644
--- a/tests/e2e/v3_curl_auth_test.go
+++ b/tests/e2e/v3_curl_auth_test.go
@@ -360,7 +360,7 @@ func testCurlV3AuthRoleManagePermission(cx ctlCtx) {
grantPermissionReq, err := json.Marshal(&pb.AuthRoleGrantPermissionRequest{
Name: rolename,
Perm: &authpb.Permission{
- PermType: authpb.READ,
+ PermType: authpb.Permission_READ,
Key: []byte("fakeKey"),
},
})
diff --git a/tests/e2e/v3_curl_kv_test.go b/tests/e2e/v3_curl_kv_test.go
index 5225fdc93b06..f98e61453410 100644
--- a/tests/e2e/v3_curl_kv_test.go
+++ b/tests/e2e/v3_curl_kv_test.go
@@ -18,7 +18,7 @@ import (
"encoding/json"
"testing"
- protov1 "github.com/golang/protobuf/proto"
+ protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
gw "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
diff --git a/tests/e2e/v3_curl_lease_test.go b/tests/e2e/v3_curl_lease_test.go
index bd9d1adc6dd8..973e9db166b8 100644
--- a/tests/e2e/v3_curl_lease_test.go
+++ b/tests/e2e/v3_curl_lease_test.go
@@ -162,7 +162,7 @@ func gwLeaseRevoke(cx ctlCtx, leaseID int64) string {
func gwKVPutLease(cx ctlCtx, k string, v string, leaseID int64) string {
d := pb.PutRequest{Key: []byte(k), Value: []byte(v), Lease: leaseID}
- s, err := e2e.DataMarshal(d)
+ s, err := e2e.DataMarshal(&d)
require.NoErrorf(cx.t, err, "gwKVPutLease: Marshal error")
return s
}
diff --git a/tests/e2e/v3_curl_maxstream_test.go b/tests/e2e/v3_curl_maxstream_test.go
index 027a3b538e8c..9b4e0f909262 100644
--- a/tests/e2e/v3_curl_maxstream_test.go
+++ b/tests/e2e/v3_curl_maxstream_test.go
@@ -25,6 +25,7 @@ import (
"time"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/encoding/protojson"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -88,7 +89,7 @@ func testCurlV3MaxStream(t *testing.T, reachLimit bool, opts ...ctlOption) {
// Step 2: create the cluster
t.Log("Creating an etcd cluster")
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t, e2e.WithConfig(&cx.cfg))
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(&cx.cfg))
require.NoErrorf(t, err, "Failed to start etcd cluster")
cx.epc = epc
cx.dataDir = epc.Procs[0].Config().DataDirPath
@@ -137,11 +138,15 @@ func testCurlV3MaxStream(t *testing.T, reachLimit bool, opts ...ctlOption) {
}
func submitConcurrentWatch(cx ctlCtx, number int, wgDone *sync.WaitGroup, closeCh chan struct{}) {
- watchData, err := json.Marshal(&pb.WatchRequest_CreateRequest{
- CreateRequest: &pb.WatchCreateRequest{
- Key: []byte("foo"),
+ watchData, err := protojson.Marshal(
+ &pb.WatchRequest{
+ RequestUnion: &pb.WatchRequest_CreateRequest{
+ CreateRequest: &pb.WatchCreateRequest{
+ Key: []byte("foo"),
+ },
+ },
},
- })
+ )
require.NoError(cx.t, err)
var wgSchedule sync.WaitGroup
diff --git a/tests/e2e/v3_curl_watch_test.go b/tests/e2e/v3_curl_watch_test.go
index f6ced632cd88..f0c20b8575e4 100644
--- a/tests/e2e/v3_curl_watch_test.go
+++ b/tests/e2e/v3_curl_watch_test.go
@@ -54,7 +54,7 @@ func testCurlV3Watch(cx ctlCtx) {
func TestCurlWatchIssue19509(t *testing.T) {
e2e.BeforeTest(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
epc, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithConfig(e2e.NewConfigClientTLS()), e2e.WithClusterSize(1))
require.NoError(t, err)
diff --git a/tests/e2e/v3_lease_no_proxy_test.go b/tests/e2e/v3_lease_no_proxy_test.go
index 0bb3939f3134..7e83ceab276e 100644
--- a/tests/e2e/v3_lease_no_proxy_test.go
+++ b/tests/e2e/v3_lease_no_proxy_test.go
@@ -57,7 +57,7 @@ func TestLeaseRevoke_ClientSwitchToOtherMember(t *testing.T) {
func testLeaseRevokeIssue(t *testing.T, clusterSize int, connectToOneFollower bool) {
e2e.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
t.Log("Starting a new etcd cluster")
epc, err := e2e.NewEtcdProcessCluster(ctx, t,
@@ -127,7 +127,7 @@ func testLeaseRevokeIssue(t *testing.T, clusterSize int, connectToOneFollower bo
err = epc.Procs[leaderIdx].Failpoints().SetupHTTP(ctx, "raftBeforeSave", `sleep("30s")`)
require.NoError(t, err)
- cctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ cctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
t.Logf("Waiting for a new leader to be elected, old leader index: %d, old leader ID: %d", leaderIdx, oldLeaderID)
testutils.ExecuteUntil(cctx, t, func() {
for {
diff --git a/tests/e2e/watch_test.go b/tests/e2e/watch_test.go
index 758177c64898..7c4c0245e8ef 100644
--- a/tests/e2e/watch_test.go
+++ b/tests/e2e/watch_test.go
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb"
@@ -95,13 +96,13 @@ func TestWatchDelayForPeriodicProgressNotification(t *testing.T) {
cfg.Client = tc.client
cfg.ClientHTTPSeparate = tc.clientHTTPSeparate
t.Run(tc.name, func(t *testing.T) {
- clus, err := e2e.NewEtcdProcessCluster(context.Background(), t, e2e.WithConfig(cfg))
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
require.NoError(t, err)
defer clus.Close()
c := newClient(t, clus.EndpointsGRPC(), tc.client)
- require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
+ require.NoError(t, fillEtcdWithData(t.Context(), c, tc.dbSizeBytes))
- ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
+ ctx, cancel := context.WithTimeout(t.Context(), watchTestDuration)
defer cancel()
g := errgroup.Group{}
continuouslyExecuteGetAll(ctx, t, &g, c)
@@ -120,13 +121,13 @@ func TestWatchDelayForManualProgressNotification(t *testing.T) {
cfg.Client = tc.client
cfg.ClientHTTPSeparate = tc.clientHTTPSeparate
t.Run(tc.name, func(t *testing.T) {
- clus, err := e2e.NewEtcdProcessCluster(context.Background(), t, e2e.WithConfig(cfg))
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
require.NoError(t, err)
defer clus.Close()
c := newClient(t, clus.EndpointsGRPC(), tc.client)
- require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
+ require.NoError(t, fillEtcdWithData(t.Context(), c, tc.dbSizeBytes))
- ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
+ ctx, cancel := context.WithTimeout(t.Context(), watchTestDuration)
defer cancel()
g := errgroup.Group{}
continuouslyExecuteGetAll(ctx, t, &g, c)
@@ -157,13 +158,13 @@ func TestWatchDelayForEvent(t *testing.T) {
cfg.Client = tc.client
cfg.ClientHTTPSeparate = tc.clientHTTPSeparate
t.Run(tc.name, func(t *testing.T) {
- clus, err := e2e.NewEtcdProcessCluster(context.Background(), t, e2e.WithConfig(cfg))
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
require.NoError(t, err)
defer clus.Close()
c := newClient(t, clus.EndpointsGRPC(), tc.client)
- require.NoError(t, fillEtcdWithData(context.Background(), c, tc.dbSizeBytes))
+ require.NoError(t, fillEtcdWithData(t.Context(), c, tc.dbSizeBytes))
- ctx, cancel := context.WithTimeout(context.Background(), watchTestDuration)
+ ctx, cancel := context.WithTimeout(t.Context(), watchTestDuration)
defer cancel()
g := errgroup.Group{}
g.Go(func() error {
@@ -226,7 +227,7 @@ func continuouslyExecuteGetAll(ctx context.Context, t *testing.T, g *errgroup.Gr
}
respSize := 0
for _, kv := range resp.Kvs {
- respSize += kv.Size()
+ respSize += proto.Size(kv)
}
mux.Lock()
size += respSize
@@ -270,14 +271,14 @@ func TestDeleteEventDrop_Issue18089(t *testing.T) {
cfg := e2e.DefaultConfig()
cfg.ClusterSize = 1
cfg.Client = e2e.ClientConfig{ConnectionType: e2e.ClientTLS}
- clus, err := e2e.NewEtcdProcessCluster(context.Background(), t, e2e.WithConfig(cfg))
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg))
require.NoError(t, err)
defer clus.Close()
c := newClient(t, clus.EndpointsGRPC(), cfg.Client)
defer c.Close()
- ctx := context.Background()
+ ctx := t.Context()
const (
key = "k"
v2 = "v2"
@@ -315,11 +316,11 @@ func TestDeleteEventDrop_Issue18089(t *testing.T) {
case watchResp := <-watchChan:
require.Len(t, watchResp.Events, 2)
- require.Equal(t, mvccpb.DELETE, watchResp.Events[0].Type)
+ require.Equal(t, mvccpb.Event_DELETE, watchResp.Events[0].Type)
deletedKey := string(watchResp.Events[0].Kv.Key)
require.Equal(t, key, deletedKey)
- require.Equal(t, mvccpb.PUT, watchResp.Events[1].Type)
+ require.Equal(t, mvccpb.Event_PUT, watchResp.Events[1].Type)
updatedKey := string(watchResp.Events[1].Kv.Key)
require.Equal(t, key, updatedKey)
@@ -345,14 +346,14 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
e2e.BeforeTest(t)
cfg := e2e.DefaultConfig()
cfg.Client = e2e.ClientConfig{ConnectionType: e2e.ClientTLS}
- clus, err := e2e.NewEtcdProcessCluster(context.Background(), t, e2e.WithConfig(cfg), e2e.WithClusterSize(1))
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(), t, e2e.WithConfig(cfg), e2e.WithClusterSize(1))
require.NoError(t, err)
defer clus.Close()
c := newClient(t, clus.EndpointsGRPC(), cfg.Client)
defer c.Close()
- ctx := context.Background()
+ ctx := t.Context()
key := "foo"
totalRev := 100
@@ -390,7 +391,7 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
assert.NoError(t, derr)
respHeader = resp.Header
- requestedValues = append(requestedValues, valueEvent{value: "", typ: mvccpb.DELETE})
+ requestedValues = append(requestedValues, valueEvent{value: "", typ: mvccpb.Event_DELETE})
} else {
value := fmt.Sprintf("%d", vi)
@@ -399,7 +400,7 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
assert.NoError(t, perr)
respHeader = resp.Header
- requestedValues = append(requestedValues, valueEvent{value: value, typ: mvccpb.PUT})
+ requestedValues = append(requestedValues, valueEvent{value: value, typ: mvccpb.Event_PUT})
}
lastRevision = respHeader.Revision
@@ -482,7 +483,7 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
updatedKey := string(ev.Kv.Key)
require.Equal(t, key, updatedKey)
- if expected.typ == mvccpb.PUT {
+ if expected.typ == mvccpb.Event_PUT {
updatedValue := string(ev.Kv.Value)
require.Equal(t, expected.value, updatedValue)
}
@@ -494,11 +495,11 @@ func testStartWatcherFromCompactedRevision(t *testing.T, performCompactOnTombsto
func TestResumeCompactionOnTombstone(t *testing.T) {
e2e.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
compactBatchLimit := 5
cfg := e2e.DefaultConfig()
- clus, err := e2e.NewEtcdProcessCluster(context.Background(),
+ clus, err := e2e.NewEtcdProcessCluster(t.Context(),
t,
e2e.WithConfig(cfg),
e2e.WithClusterSize(1),
@@ -532,7 +533,7 @@ func TestResumeCompactionOnTombstone(t *testing.T) {
case watchResp := <-c1.Watch(ctx, firstKey, clientv3.WithRev(deleteResp.Header.Revision)):
require.Len(t, watchResp.Events, 1)
- require.Equal(t, mvccpb.DELETE, watchResp.Events[0].Type)
+ require.Equal(t, mvccpb.Event_DELETE, watchResp.Events[0].Type)
deletedKey := string(watchResp.Events[0].Kv.Key)
require.Equal(t, firstKey, deletedKey)
diff --git a/tests/e2e/zap_logging_test.go b/tests/e2e/zap_logging_test.go
index 6752a23cc2f5..0d7b7ffed501 100644
--- a/tests/e2e/zap_logging_test.go
+++ b/tests/e2e/zap_logging_test.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"encoding/json"
"testing"
"time"
@@ -29,7 +28,7 @@ import (
func TestServerJsonLogging(t *testing.T) {
e2e.BeforeTest(t)
- epc, err := e2e.NewEtcdProcessCluster(context.TODO(), t,
+ epc, err := e2e.NewEtcdProcessCluster(t.Context(), t,
e2e.WithClusterSize(1),
e2e.WithLogLevel("debug"),
)
@@ -115,7 +114,7 @@ func TestConnectionRejectMessage(t *testing.T) {
t.Log("Starting an etcd process and wait for it to get ready.")
p, err := e2e.SpawnCmd(commonArgs, nil)
require.NoError(t, err)
- err = e2e.WaitReadyExpectProc(context.TODO(), p, e2e.EtcdServerReadyLines)
+ err = e2e.WaitReadyExpectProc(t.Context(), p, e2e.EtcdServerReadyLines)
require.NoError(t, err)
defer func() {
p.Stop()
@@ -127,7 +126,7 @@ func TestConnectionRejectMessage(t *testing.T) {
doneCh := make(chan struct{}, 1)
go func() {
startedCh <- struct{}{}
- verr := e2e.WaitReadyExpectProc(context.TODO(), p, []string{tc.expectedErrMsg})
+ verr := e2e.WaitReadyExpectProc(t.Context(), p, []string{tc.expectedErrMsg})
assert.NoError(t, verr)
doneCh <- struct{}{}
}()
diff --git a/tests/fixtures/gencerts.sh b/tests/fixtures/gencerts.sh
index 79c0680e6cec..f7ca54ad927b 100755
--- a/tests/fixtures/gencerts.sh
+++ b/tests/fixtures/gencerts.sh
@@ -22,7 +22,7 @@ fi
cfssl gencert --initca=true ./ca-csr.json | cfssljson --bare ./ca
mv ca.pem ca.crt
-if which openssl >/dev/null; then
+if command -v openssl >/dev/null; then
openssl x509 -in ca.crt -noout -text
fi
diff --git a/tests/framework/config/client.go b/tests/framework/config/client.go
index ac82bd547925..a7a0048638c5 100644
--- a/tests/framework/config/client.go
+++ b/tests/framework/config/client.go
@@ -28,16 +28,22 @@ import (
type ClientOption func(any)
type GetOptions struct {
- Revision int
- End string
- CountOnly bool
- Serializable bool
- Prefix bool
- FromKey bool
- Limit int
- Order clientv3.SortOrder
- SortBy clientv3.SortTarget
- Timeout time.Duration
+ Revision int
+ End string
+ CountOnly bool
+ Serializable bool
+ Prefix bool
+ FromKey bool
+ Limit int
+ Order clientv3.SortOrder
+ SortBy clientv3.SortTarget
+ Timeout time.Duration
+ KeysOnly bool
+ MinModRevision int
+ MaxModRevision int
+ MinCreateRevision int
+ MaxCreateRevision int
+ Stream bool
}
type PutOptions struct {
diff --git a/tests/framework/e2e/cluster.go b/tests/framework/e2e/cluster.go
index 85230be68e1f..73f48a5bd892 100644
--- a/tests/framework/e2e/cluster.go
+++ b/tests/framework/e2e/cluster.go
@@ -16,7 +16,6 @@ package e2e
import (
"context"
- "errors"
"flag"
"fmt"
"maps"
@@ -56,6 +55,7 @@ type ClientConfig struct {
CertAuthority bool
AutoTLS bool
RevokeCerts bool
+ DialTimeout time.Duration
}
// allow alphanumerics, underscores and dashes
@@ -177,11 +177,6 @@ type EtcdProcessClusterConfig struct {
IsPeerTLS bool
IsPeerAutoTLS bool
CN bool
-
- // Removed in v3.6
-
- Discovery string // v2 discovery
- EnableV2 bool
}
func DefaultConfig() *EtcdProcessClusterConfig {
@@ -287,10 +282,6 @@ func WithStrictReconfigCheck(strict bool) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.StrictReconfigCheck = strict }
}
-func WithEnableV2(enable bool) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.EnableV2 = enable }
-}
-
func WithAuthTokenOpts(token string) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.AuthToken = token }
}
@@ -299,10 +290,6 @@ func WithRollingStart(rolling bool) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.RollingStart = rolling }
}
-func WithDiscovery(discovery string) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.Discovery = discovery }
-}
-
func WithDiscoveryEndpoints(endpoints []string) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.DiscoveryCfg.Endpoints = endpoints }
}
@@ -319,20 +306,20 @@ func WithCorruptCheckTime(time time.Duration) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.CorruptCheckTime = time }
}
-func WithExperimentalCorruptCheckTime(time time.Duration) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalCorruptCheckTime = time }
-}
-
func WithInitialClusterToken(token string) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.InitialClusterToken = token }
}
func WithInitialCorruptCheck(enabled bool) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalInitialCorruptCheck = enabled }
+ return func(c *EtcdProcessClusterConfig) {
+ c.ServerConfig.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("InitialCorruptCheck=%t", enabled))
+ }
}
func WithCompactHashCheckEnabled(enabled bool) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalCompactHashCheckEnabled = enabled }
+ return func(c *EtcdProcessClusterConfig) {
+ c.ServerConfig.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("CompactHashCheck=%t", enabled))
+ }
}
func WithCompactHashCheckTime(time time.Duration) EPClusterOption {
@@ -355,18 +342,6 @@ func WithWarningUnaryRequestDuration(time time.Duration) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.WarningUnaryRequestDuration = time }
}
-// WithExperimentalWarningUnaryRequestDuration sets a value for `-experimental-warning-unary-request-duration`.
-// TODO(ahrtr): remove this function when the corresponding experimental flag is decommissioned.
-func WithExperimentalWarningUnaryRequestDuration(time time.Duration) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalWarningUnaryRequestDuration = time }
-}
-
-func WithExperimentalStopGRPCServiceOnDefrag(stopGRPCServiceOnDefrag bool) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) {
- c.ServerConfig.ExperimentalStopGRPCServiceOnDefrag = stopGRPCServiceOnDefrag
- }
-}
-
func WithServerFeatureGate(featureName string, val bool) EPClusterOption {
return func(c *EtcdProcessClusterConfig) {
if err := c.ServerConfig.ServerFeatureGate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", featureName, val)); err != nil {
@@ -379,22 +354,14 @@ func WithCompactionBatchLimit(limit int) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.CompactionBatchLimit = limit }
}
-func WithExperimentalCompactionBatchLimit(limit int) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalCompactionBatchLimit = limit }
-}
-
func WithCompactionSleepInterval(time time.Duration) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalCompactionSleepInterval = time }
+ return func(c *EtcdProcessClusterConfig) { c.ServerConfig.CompactionSleepInterval = time }
}
func WithWatchProcessNotifyInterval(interval time.Duration) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.WatchProgressNotifyInterval = interval }
}
-func WithExperimentalWatchProcessNotifyInterval(interval time.Duration) EPClusterOption {
- return func(c *EtcdProcessClusterConfig) { c.ServerConfig.ExperimentalWatchProgressNotifyInterval = interval }
-}
-
func WithEnvVars(ev map[string]string) EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.EnvVars = ev }
}
@@ -423,25 +390,34 @@ func WithExtensiveMetrics() EPClusterOption {
return func(c *EtcdProcessClusterConfig) { c.ServerConfig.Metrics = "extensive" }
}
+func WithEnableDistributedTracing(addr string) EPClusterOption {
+ return func(c *EtcdProcessClusterConfig) {
+ c.ServerConfig.EnableDistributedTracing = true
+ c.ServerConfig.DistributedTracingServiceName = "etcd"
+ c.ServerConfig.DistributedTracingAddress = addr
+ c.ServerConfig.DistributedTracingSamplingRatePerMillion = 1_000_000
+ }
+}
+
// NewEtcdProcessCluster launches a new cluster from etcd processes, returning
// a new EtcdProcessCluster once all nodes are ready to accept client requests.
-func NewEtcdProcessCluster(ctx context.Context, t testing.TB, opts ...EPClusterOption) (*EtcdProcessCluster, error) {
+func NewEtcdProcessCluster(ctx context.Context, tb testing.TB, opts ...EPClusterOption) (*EtcdProcessCluster, error) {
cfg := NewConfig(opts...)
- epc, err := InitEtcdProcessCluster(t, cfg)
+ epc, err := InitEtcdProcessCluster(tb, cfg)
if err != nil {
return nil, err
}
- return StartEtcdProcessCluster(ctx, t, epc, cfg)
+ return StartEtcdProcessCluster(ctx, tb, epc, cfg)
}
// InitEtcdProcessCluster initializes a new cluster based on the given config.
// It doesn't start the cluster.
-func InitEtcdProcessCluster(t testing.TB, cfg *EtcdProcessClusterConfig) (*EtcdProcessCluster, error) {
- SkipInShortMode(t)
+func InitEtcdProcessCluster(tb testing.TB, cfg *EtcdProcessClusterConfig) (*EtcdProcessCluster, error) {
+ SkipInShortMode(tb)
if cfg.Logger == nil {
- cfg.Logger = zaptest.NewLogger(t)
+ cfg.Logger = zaptest.NewLogger(tb)
}
if cfg.BasePort == 0 {
cfg.BasePort = EtcdProcessBasePort
@@ -460,17 +436,17 @@ func InitEtcdProcessCluster(t testing.TB, cfg *EtcdProcessClusterConfig) (*EtcdP
}
}
- etcdCfgs := cfg.EtcdAllServerProcessConfigs(t)
+ etcdCfgs := cfg.EtcdAllServerProcessConfigs(tb)
epc := &EtcdProcessCluster{
Cfg: cfg,
- lg: zaptest.NewLogger(t),
+ lg: zaptest.NewLogger(tb),
Procs: make([]EtcdProcess, cfg.ClusterSize),
nextSeq: cfg.ClusterSize,
}
// launch etcd processes
for i := range etcdCfgs {
- proc, err := NewEtcdProcess(t, etcdCfgs[i])
+ proc, err := NewEtcdProcess(tb, etcdCfgs[i])
if err != nil {
epc.Close()
return nil, fmt.Errorf("cannot configure: %w", err)
@@ -482,7 +458,7 @@ func InitEtcdProcessCluster(t testing.TB, cfg *EtcdProcessClusterConfig) (*EtcdP
}
// StartEtcdProcessCluster launches a new cluster from etcd processes.
-func StartEtcdProcessCluster(ctx context.Context, t testing.TB, epc *EtcdProcessCluster, cfg *EtcdProcessClusterConfig) (*EtcdProcessCluster, error) {
+func StartEtcdProcessCluster(ctx context.Context, tb testing.TB, epc *EtcdProcessCluster, cfg *EtcdProcessClusterConfig) (*EtcdProcessCluster, error) {
if cfg.RollingStart {
if err := epc.RollingStart(ctx); err != nil {
return nil, fmt.Errorf("cannot rolling-start: %w", err)
@@ -496,11 +472,11 @@ func StartEtcdProcessCluster(ctx context.Context, t testing.TB, epc *EtcdProcess
for _, proc := range epc.Procs {
if cfg.GoFailEnabled && !proc.Failpoints().Enabled() {
epc.Close()
- t.Skip("please run 'make gofail-enable && make build' before running the test")
+ tb.Skip("please run 'make gofail-enable && make build' before running the test")
}
}
if cfg.InitialLeaderIndex >= 0 {
- if err := epc.MoveLeader(ctx, t, cfg.InitialLeaderIndex); err != nil {
+ if err := epc.MoveLeader(ctx, tb, cfg.InitialLeaderIndex); err != nil {
return nil, fmt.Errorf("failed to move leader: %w", err)
}
}
@@ -532,7 +508,7 @@ func (cfg *EtcdProcessClusterConfig) EtcdAllServerProcessConfigs(tb testing.TB)
}
func (cfg *EtcdProcessClusterConfig) SetInitialOrDiscovery(serverCfg *EtcdServerProcessConfig, initialCluster []string, initialClusterState string) {
- if cfg.Discovery == "" && len(cfg.ServerConfig.DiscoveryCfg.Endpoints) == 0 {
+ if len(cfg.ServerConfig.DiscoveryCfg.Endpoints) == 0 {
serverCfg.InitialCluster = strings.Join(initialCluster, ",")
serverCfg.Args = append(serverCfg.Args, "--initial-cluster="+serverCfg.InitialCluster)
serverCfg.Args = append(serverCfg.Args, "--initial-cluster-state="+initialClusterState)
@@ -598,7 +574,7 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
"--listen-peer-urls=" + peerListenURL.String(),
"--initial-advertise-peer-urls=" + peerAdvertiseURL.String(),
"--initial-cluster-token=" + cfg.ServerConfig.InitialClusterToken,
- "--data-dir", dataDirPath,
+ "--data-dir=" + dataDirPath,
"--snapshot-count=" + fmt.Sprintf("%d", cfg.ServerConfig.SnapshotCount),
}
var clientHTTPURL string
@@ -618,9 +594,15 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
if !cfg.ServerConfig.StrictReconfigCheck {
args = append(args, "--strict-reconfig-check=false")
}
- if cfg.EnableV2 {
- args = append(args, "--enable-v2=true")
+ if cfg.ServerConfig.EnableDistributedTracing {
+ args = append(args,
+ "--enable-distributed-tracing",
+ fmt.Sprintf("--distributed-tracing-address=%s", cfg.ServerConfig.DistributedTracingAddress),
+ fmt.Sprintf("--distributed-tracing-service-name=%s", cfg.ServerConfig.DistributedTracingServiceName),
+ fmt.Sprintf("--distributed-tracing-sampling-rate=%d", cfg.ServerConfig.DistributedTracingSamplingRatePerMillion),
+ )
}
+
var murl string
if cfg.MetricsURLScheme != "" {
murl = (&url.URL{
@@ -632,19 +614,24 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
args = append(args, cfg.TLSArgs()...)
- if cfg.Discovery != "" {
- args = append(args, "--discovery="+cfg.Discovery)
- }
-
execPath := cfg.binaryPath(i)
if cfg.ServerConfig.SnapshotCatchUpEntries != etcdserver.DefaultSnapshotCatchUpEntries {
if !IsSnapshotCatchupEntriesFlagAvailable(execPath) {
- cfg.ServerConfig.ExperimentalSnapshotCatchUpEntries = cfg.ServerConfig.SnapshotCatchUpEntries
cfg.ServerConfig.SnapshotCatchUpEntries = etcdserver.DefaultSnapshotCatchUpEntries
}
}
+ var (
+ binaryVersion *semver.Version
+ err error
+ )
+ if execPath != "" {
+ binaryVersion, err = GetVersionFromBinary(execPath)
+ if err != nil {
+ tb.Logf("Failed to get binary version from %s: %v", execPath, err)
+ }
+ }
defaultValues := values(*embed.NewConfig())
overrideValues := values(cfg.ServerConfig)
for flag, value := range overrideValues {
@@ -654,7 +641,7 @@ func (cfg *EtcdProcessClusterConfig) EtcdServerProcessConfig(tb testing.TB, i in
if strings.HasSuffix(flag, "snapshot-catchup-entries") && !CouldSetSnapshotCatchupEntries(execPath) {
continue
}
- args = append(args, fmt.Sprintf("--%s=%s", flag, value))
+ args = append(args, convertFlag(flag, value, binaryVersion))
}
envVars := map[string]string{}
maps.Copy(envVars, cfg.EnvVars)
@@ -847,19 +834,21 @@ func (epc *EtcdProcessCluster) CloseProc(ctx context.Context, finder func(EtcdPr
return fmt.Errorf("failed to find member ID: %w", err)
}
+ sleepDuration := 500 * time.Millisecond
+ maxRetries := int((2 * etcdserver.HealthInterval) / sleepDuration)
memberRemoved := false
- for i := 0; i < 10; i++ {
+ for i := 0; i < maxRetries; i++ {
_, err := memberCtl.MemberRemove(ctx, memberID)
if err != nil && strings.Contains(err.Error(), "member not found") {
memberRemoved = true
break
}
- time.Sleep(500 * time.Millisecond)
+ time.Sleep(sleepDuration)
}
if !memberRemoved {
- return errors.New("failed to remove member after 10 tries")
+ return fmt.Errorf("failed to remove member after %d tries", maxRetries)
}
epc.lg.Info("successfully removed member", zap.String("acurl", proc.Config().ClientURL))
@@ -960,6 +949,16 @@ func (epc *EtcdProcessCluster) UpdateProcOptions(i int, tb testing.TB, opts ...E
return nil
}
+func PatchArgs(args []string, flag, newValue string) error {
+ for i, arg := range args {
+ if strings.Contains(arg, flag) {
+ args[i] = fmt.Sprintf("--%s=%s", flag, newValue)
+ return nil
+ }
+ }
+ return fmt.Errorf("--%s flag not found", flag)
+}
+
func (epc *EtcdProcessCluster) Start(ctx context.Context) error {
return epc.start(func(ep EtcdProcess) error { return ep.Start(ctx) })
}
@@ -1110,29 +1109,29 @@ func findMemberIDByEndpoint(members []*etcdserverpb.Member, endpoint string) (ui
// WaitLeader returns index of the member in c.Members() that is leader
// or fails the test (if not established in 30s).
-func (epc *EtcdProcessCluster) WaitLeader(t testing.TB) int {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+func (epc *EtcdProcessCluster) WaitLeader(tb testing.TB) int {
+ ctx, cancel := context.WithTimeout(tb.Context(), 30*time.Second)
defer cancel()
- return epc.WaitMembersForLeader(ctx, t, epc.Procs)
+ return epc.WaitMembersForLeader(ctx, tb, epc.Procs)
}
// WaitMembersForLeader waits until given members agree on the same leader,
// and returns its 'index' in the 'membs' list
-func (epc *EtcdProcessCluster) WaitMembersForLeader(ctx context.Context, t testing.TB, membs []EtcdProcess) int {
+func (epc *EtcdProcessCluster) WaitMembersForLeader(ctx context.Context, tb testing.TB, membs []EtcdProcess) int {
cc := epc.Etcdctl()
// ensure leader is up via linearizable get
for {
select {
case <-ctx.Done():
- t.Fatal("WaitMembersForLeader timeout")
+ tb.Fatal("WaitMembersForLeader timeout")
default:
}
_, err := cc.Get(ctx, "0", config.GetOptions{Timeout: 10*config.TickDuration + time.Second})
if err == nil || strings.Contains(err.Error(), "Key not found") {
break
}
- t.Logf("WaitMembersForLeader Get err: %v", err)
+ tb.Logf("WaitMembersForLeader Get err: %v", err)
}
leaders := make(map[uint64]struct{})
@@ -1140,17 +1139,22 @@ func (epc *EtcdProcessCluster) WaitMembersForLeader(ctx context.Context, t testi
for {
select {
case <-ctx.Done():
- t.Fatal("WaitMembersForLeader timeout")
+ tb.Fatal("WaitMembersForLeader timeout")
default:
}
for i := range membs {
+ if !membs[i].IsRunning() {
+ // if member[i] has stopped
+ continue
+ }
+
resp, err := membs[i].Etcdctl().Status(ctx)
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
// if member[i] has stopped
continue
}
- t.Fatal(err)
+ tb.Fatal(err)
}
members[resp[0].Header.MemberId] = i
leaders[resp[0].Leader] = struct{}{}
@@ -1165,24 +1169,24 @@ func (epc *EtcdProcessCluster) WaitMembersForLeader(ctx context.Context, t testi
}
for l := range leaders {
if index, ok := members[l]; ok {
- t.Logf("members agree on a leader, members:%v , leader:%v", members, l)
+ tb.Logf("members agree on a leader, members:%v , leader:%v", members, l)
return index
}
- t.Fatalf("members agree on a leader which is not one of members, members:%v , leader:%v", members, l)
+ tb.Fatalf("members agree on a leader which is not one of members, members:%v , leader:%v", members, l)
}
- t.Fatal("impossible path of execution")
+ tb.Fatal("impossible path of execution")
return -1
}
// MoveLeader moves the leader to the ith process.
-func (epc *EtcdProcessCluster) MoveLeader(ctx context.Context, t testing.TB, i int) error {
+func (epc *EtcdProcessCluster) MoveLeader(ctx context.Context, tb testing.TB, i int) error {
if i < 0 || i >= len(epc.Procs) {
return fmt.Errorf("invalid index: %d, must between 0 and %d", i, len(epc.Procs)-1)
}
- t.Logf("moving leader to Procs[%d]", i)
- oldLeader := epc.WaitMembersForLeader(ctx, t, epc.Procs)
+ tb.Logf("moving leader to Procs[%d]", i)
+ oldLeader := epc.WaitMembersForLeader(ctx, tb, epc.Procs)
if oldLeader == i {
- t.Logf("Procs[%d] is already the leader", i)
+ tb.Logf("Procs[%d] is already the leader", i)
return nil
}
resp, err := epc.Procs[i].Etcdctl().Status(ctx)
@@ -1194,10 +1198,10 @@ func (epc *EtcdProcessCluster) MoveLeader(ctx context.Context, t testing.TB, i i
if err != nil {
return err
}
- newLeader := epc.WaitMembersForLeader(ctx, t, epc.Procs)
+ newLeader := epc.WaitMembersForLeader(ctx, tb, epc.Procs)
if newLeader != i {
- t.Fatalf("expect new leader to be Procs[%d] but got Procs[%d]", i, newLeader)
+ tb.Fatalf("expect new leader to be Procs[%d] but got Procs[%d]", i, newLeader)
}
- t.Logf("moved leader from Procs[%d] to Procs[%d]", oldLeader, i)
+ tb.Logf("moved leader from Procs[%d] to Procs[%d]", oldLeader, i)
return nil
}
diff --git a/tests/framework/e2e/cluster_direct.go b/tests/framework/e2e/cluster_direct.go
index 70c60dbf4c0a..ea46ea31d010 100644
--- a/tests/framework/e2e/cluster_direct.go
+++ b/tests/framework/e2e/cluster_direct.go
@@ -18,6 +18,6 @@ package e2e
import "testing"
-func NewEtcdProcess(t testing.TB, cfg *EtcdServerProcessConfig) (EtcdProcess, error) {
- return NewEtcdServerProcess(t, cfg)
+func NewEtcdProcess(tb testing.TB, cfg *EtcdServerProcessConfig) (EtcdProcess, error) {
+ return NewEtcdServerProcess(tb, cfg)
}
diff --git a/tests/framework/e2e/cluster_test.go b/tests/framework/e2e/cluster_test.go
index 24fa8a91bc6c..1691a3458f68 100644
--- a/tests/framework/e2e/cluster_test.go
+++ b/tests/framework/e2e/cluster_test.go
@@ -23,8 +23,8 @@ import (
)
func TestEtcdServerProcessConfig(t *testing.T) {
- v3_5_12 := semver.Version{Major: 3, Minor: 5, Patch: 12}
- v3_5_14 := semver.Version{Major: 3, Minor: 5, Patch: 14}
+ v3_6_0 := semver.Version{Major: 3, Minor: 6, Patch: 0}
+ v3_7_0 := semver.Version{Major: 3, Minor: 7, Patch: 0}
tcs := []struct {
name string
config *EtcdProcessClusterConfig
@@ -43,8 +43,7 @@ func TestEtcdServerProcessConfig(t *testing.T) {
"--listen-peer-urls=http://localhost:1",
"--initial-advertise-peer-urls=http://localhost:1",
"--initial-cluster-token=new",
- "--data-dir",
- "/tmp/a/member-0",
+ "--data-dir=/tmp/a/member-0",
"--snapshot-count=10000",
"--initial-cluster-token=new",
},
@@ -67,7 +66,7 @@ func TestEtcdServerProcessConfig(t *testing.T) {
name: "CorruptCheck",
config: NewConfig(WithInitialCorruptCheck(true)),
expectArgsContain: []string{
- "--experimental-initial-corrupt-check=true",
+ "--feature-gates=InitialCorruptCheck=true",
},
},
{
@@ -81,9 +80,9 @@ func TestEtcdServerProcessConfig(t *testing.T) {
name: "CatchUpEntries",
config: NewConfig(WithSnapshotCatchUpEntries(100)),
expectArgsContain: []string{
- "--experimental-snapshot-catchup-entries=100",
+ "--snapshot-catchup-entries=100",
},
- mockBinaryVersion: &v3_5_14,
+ mockBinaryVersion: &v3_7_0,
},
{
name: "CatchUpEntriesNoVersion",
@@ -95,10 +94,10 @@ func TestEtcdServerProcessConfig(t *testing.T) {
{
name: "CatchUpEntriesOldVersion",
config: NewConfig(WithSnapshotCatchUpEntries(100), WithVersion(LastVersion)),
- expectArgsNotContain: []string{
+ expectArgsContain: []string{
"--snapshot-catchup-entries=100",
},
- mockBinaryVersion: &v3_5_12,
+ mockBinaryVersion: &v3_6_0,
},
{
name: "ClientHTTPSeparate",
@@ -114,13 +113,6 @@ func TestEtcdServerProcessConfig(t *testing.T) {
"--force-new-cluster=true",
},
},
- {
- name: "EnableV2",
- config: NewConfig(WithEnableV2(true)),
- expectArgsContain: []string{
- "--enable-v2=true",
- },
- },
{
name: "MetricsURL",
config: NewConfig(WithMetricsURLScheme("http")),
@@ -128,13 +120,6 @@ func TestEtcdServerProcessConfig(t *testing.T) {
"--listen-metrics-urls=http://localhost:2",
},
},
- {
- name: "Discovery",
- config: NewConfig(WithDiscovery("123")),
- expectArgsContain: []string{
- "--discovery=123",
- },
- },
{
name: "ClientTLS",
config: NewConfig(WithClientConnType(ClientTLS)),
diff --git a/tests/framework/e2e/config.go b/tests/framework/e2e/config.go
index 646a868e9ef3..d05d9a1058c8 100644
--- a/tests/framework/e2e/config.go
+++ b/tests/framework/e2e/config.go
@@ -14,6 +14,16 @@
package e2e
+import (
+ "fmt"
+ "strings"
+
+ "github.com/coreos/go-semver/semver"
+
+ "go.etcd.io/etcd/api/v3/version"
+ "go.etcd.io/etcd/tests/v3/framework/config"
+)
+
type ClusterVersion string
const (
@@ -32,4 +42,99 @@ func (cv ClusterVersion) String() string {
type ClusterContext struct {
Version ClusterVersion
+ EnvVars map[string]string
+ UseUnix bool
+}
+
+func WithHTTP2Debug() config.ClusterOption {
+ return func(c *config.ClusterConfig) {
+ ctx := ensureE2EClusterContext(c)
+ if ctx.EnvVars == nil {
+ ctx.EnvVars = map[string]string{}
+ }
+ // Enable debug mode to get logs with http2 headers (including authority)
+ ctx.EnvVars["GODEBUG"] = "http2debug=2"
+ c.ClusterContext = ctx
+ }
+}
+
+func WithUnixClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {
+ ctx := ensureE2EClusterContext(c)
+ ctx.UseUnix = true
+ c.ClusterContext = ctx
+ }
+}
+
+func WithTCPClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {
+ ctx := ensureE2EClusterContext(c)
+ ctx.UseUnix = false
+ c.ClusterContext = ctx
+ }
+}
+
+func ensureE2EClusterContext(c *config.ClusterConfig) *ClusterContext {
+ ctx, _ := c.ClusterContext.(*ClusterContext)
+ if ctx == nil {
+ ctx = &ClusterContext{}
+ }
+ return ctx
+}
+
+var experimentalFlags = map[string]struct{}{
+ "compact-hash-check-time": {},
+ "corrupt-check-time": {},
+ "compaction-batch-limit": {},
+ "watch-progress-notify-interval": {},
+ "warning-apply-duration": {},
+ "bootstrap-defrag-threshold-megabytes": {},
+ "memory-mlock": {},
+ "snapshot-catchup-entries": {},
+ "compaction-sleep-interval": {},
+ "downgrade-check-time": {},
+ "peer-skip-client-san-verification": {},
+ "enable-distributed-tracing": {},
+ "distributed-tracing-address": {},
+ "distributed-tracing-service-name": {},
+ "distributed-tracing-instance-id": {},
+ "distributed-tracing-sampling-rate": {},
+}
+
+// convertFlag converts between experimental and non-experimental flags.
+// All experimental flags have been removed in version 3.7, but versions prior
+// to 3.6 only support experimental flags. The robustness tests use the same
+// code from the main branch to test all previously supported releases, so we
+// need to convert flags accordingly based on the binary version.
+func convertFlag(name, value string, ver *semver.Version) string {
+ // For versions >= 3.6, use the normal (non-experimental) flag.
+ if ver == nil || !ver.LessThan(version.V3_6) {
+ return fmt.Sprintf("--%s=%s", name, value)
+ }
+
+ // For versions < 3.6, use the experimental flag if it exists in `experimentalFlags`
+ if _, ok := experimentalFlags[name]; ok {
+ return fmt.Sprintf("--experimental-%s=%s", name, value)
+ }
+
+ return fmt.Sprintf("--%s=%s", name, value)
+}
+
+func convertFlags(args []string, ver *semver.Version) []string {
+ var retArgs []string
+
+ for _, arg := range args {
+ kv := strings.Split(arg, "=")
+ if len(kv) != 2 {
+ retArgs = append(retArgs, arg)
+ continue
+ }
+
+ name := strings.TrimPrefix(kv[0], "--")
+ name = strings.TrimPrefix(name, "experimental-")
+
+ retArgs = append(retArgs, convertFlag(name, kv[1], ver))
+ }
+
+ return retArgs
}
diff --git a/tests/framework/e2e/downgrade.go b/tests/framework/e2e/downgrade.go
index fea4702773de..ecc22d591847 100644
--- a/tests/framework/e2e/downgrade.go
+++ b/tests/framework/e2e/downgrade.go
@@ -15,7 +15,6 @@
package e2e
import (
- "context"
"encoding/json"
"fmt"
"math/rand"
@@ -38,7 +37,7 @@ func DowngradeEnable(t *testing.T, epc *EtcdProcessCluster, ver *semver.Version)
t.Logf("etcdctl downgrade enable %s", ver.String())
c := epc.Etcdctl()
testutils.ExecuteWithTimeout(t, 20*time.Second, func() {
- err := c.DowngradeEnable(context.TODO(), ver.String())
+ err := c.DowngradeEnable(t.Context(), ver.String())
require.NoError(t, err)
})
@@ -61,7 +60,7 @@ func DowngradeCancel(t *testing.T, epc *EtcdProcessCluster) {
testutils.ExecuteWithTimeout(t, 1*time.Minute, func() {
for {
t.Logf("etcdctl downgrade cancel")
- err = c.DowngradeCancel(context.TODO())
+ err = c.DowngradeCancel(t.Context())
if err != nil {
if strings.Contains(err.Error(), "no inflight downgrade job") {
// cancellation has been performed successfully
@@ -94,7 +93,7 @@ func ValidateDowngradeInfo(t *testing.T, clus *EtcdProcessCluster, expected *pb.
testutils.ExecuteWithTimeout(t, 1*time.Minute, func() {
for {
- statuses, err := mc.Status(context.Background())
+ statuses, err := mc.Status(t.Context())
if err != nil {
cfg.Logger.Warn("failed to get member status and retrying",
zap.Error(err),
@@ -147,6 +146,12 @@ func DowngradeUpgradeMembersByID(t *testing.T, lg *zap.Logger, clus *EtcdProcess
opString = "downgrading"
newExecPath = BinPath.EtcdLastRelease
}
+
+ binaryVersion, err := GetVersionFromBinary(newExecPath)
+ if err != nil {
+ return fmt.Errorf("failed to get binary version from %s: %w", newExecPath, err)
+ }
+
g := new(errgroup.Group)
for _, memberID := range membersToChange {
member := clus.Procs[memberID]
@@ -157,13 +162,18 @@ func DowngradeUpgradeMembersByID(t *testing.T, lg *zap.Logger, clus *EtcdProcess
if err := member.Stop(); err != nil {
return err
}
+
+ // When we downgrade or upgrade a member, we need to re-generate the flags, to convert some non-experimental
+ // flags to experimental flags, or vice verse.
+ member.Config().Args = convertFlags(member.Config().Args, binaryVersion)
+
member.Config().ExecPath = newExecPath
lg.Info("Restarting member", zap.String("member", member.Config().Name))
// We shouldn't block on waiting for the member to be ready,
// otherwise it will be blocked forever if other members are
// not started yet.
g.Go(func() error {
- return member.Start(context.TODO())
+ return member.Start(t.Context())
})
}
if err := g.Wait(); err != nil {
diff --git a/tests/framework/e2e/e2e.go b/tests/framework/e2e/e2e.go
index f78df57926ea..7ab52006beac 100644
--- a/tests/framework/e2e/e2e.go
+++ b/tests/framework/e2e/e2e.go
@@ -16,10 +16,17 @@ package e2e
import (
"context"
+ "fmt"
+ "net/url"
"os"
+ "strings"
"testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
"go.etcd.io/etcd/client/pkg/v3/testutil"
+ "go.etcd.io/etcd/pkg/v3/expect"
"go.etcd.io/etcd/tests/v3/framework/config"
intf "go.etcd.io/etcd/tests/v3/framework/interfaces"
)
@@ -39,11 +46,11 @@ func (e e2eRunner) TestMain(m *testing.M) {
os.Exit(v)
}
-func (e e2eRunner) BeforeTest(t testing.TB) {
- BeforeTest(t)
+func (e e2eRunner) BeforeTest(tb testing.TB) {
+ BeforeTest(tb)
}
-func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.ClusterOption) intf.Cluster {
+func (e e2eRunner) NewCluster(ctx context.Context, tb testing.TB, opts ...config.ClusterOption) intf.Cluster {
cfg := config.NewClusterConfig(opts...)
e2eConfig := NewConfig(
WithClusterSize(cfg.ClusterSize),
@@ -53,9 +60,12 @@ func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.
WithSnapshotCount(cfg.SnapshotCount),
)
- if cfg.ClusterContext != nil {
- e2eClusterCtx := cfg.ClusterContext.(*ClusterContext)
- e2eConfig.Version = e2eClusterCtx.Version
+ if ctx, ok := cfg.ClusterContext.(*ClusterContext); ok && ctx != nil {
+ e2eConfig.Version = ctx.Version
+ e2eConfig.EnvVars = ctx.EnvVars
+ if ctx.UseUnix {
+ e2eConfig.BaseClientScheme = "unix"
+ }
}
switch cfg.ClientTLS {
@@ -68,7 +78,7 @@ func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.
e2eConfig.Client.AutoTLS = false
e2eConfig.Client.ConnectionType = ClientTLS
default:
- t.Fatalf("ClientTLS config %q not supported", cfg.ClientTLS)
+ tb.Fatalf("ClientTLS config %q not supported", cfg.ClientTLS)
}
switch cfg.PeerTLS {
case config.NoTLS:
@@ -81,13 +91,13 @@ func (e e2eRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.
e2eConfig.IsPeerTLS = true
e2eConfig.IsPeerAutoTLS = false
default:
- t.Fatalf("PeerTLS config %q not supported", cfg.PeerTLS)
+ tb.Fatalf("PeerTLS config %q not supported", cfg.PeerTLS)
}
- epc, err := NewEtcdProcessCluster(ctx, t, WithConfig(e2eConfig))
+ epc, err := NewEtcdProcessCluster(ctx, tb, WithConfig(e2eConfig))
if err != nil {
- t.Fatalf("could not start etcd integrationCluster: %s", err)
+ tb.Fatalf("could not start etcd integrationCluster: %s", err)
}
- return &e2eCluster{t, *epc}
+ return &e2eCluster{tb, *epc}
}
type e2eCluster struct {
@@ -111,6 +121,32 @@ func (c *e2eCluster) Members() (ms []intf.Member) {
return ms
}
+func (c *e2eCluster) TemplateEndpoints(tb testing.TB, pattern string) []string {
+ tb.Helper()
+ var endpoints []string
+ for i := 0; i < c.Cfg.ClusterSize; i++ {
+ ent := pattern
+ ent = strings.ReplaceAll(ent, "${MEMBER_PORT}", fmt.Sprintf("%d", EtcdProcessBasePort+i*5))
+ endpoints = append(endpoints, ent)
+ }
+ return endpoints
+}
+
+func (c *e2eCluster) AssertAuthority(tb testing.TB, expectAuthorityPattern string) {
+ for i := range c.Procs {
+ line, _ := c.Procs[i].Logs().ExpectWithContext(tb.Context(), expect.ExpectedResponse{
+ Value: `http2: decoded hpack field header field ":authority"`,
+ })
+ line = strings.TrimSuffix(strings.TrimSuffix(line, "\n"), "\r")
+
+ u, err := url.Parse(c.Procs[i].EndpointsGRPC()[0])
+ require.NoError(tb, err)
+ expectAuthority := strings.ReplaceAll(expectAuthorityPattern, "${MEMBER_PORT}", u.Port())
+ expectLine := fmt.Sprintf(`http2: decoded hpack field header field ":authority" = %q`, expectAuthority)
+ assert.Truef(tb, strings.HasSuffix(line, expectLine), "Got %q expected suffix %q", line, expectLine)
+ }
+}
+
type e2eClient struct {
*EtcdctlV3
}
diff --git a/tests/framework/e2e/etcd_process.go b/tests/framework/e2e/etcd_process.go
index ed69e3fb875b..7a85b29b6cb4 100644
--- a/tests/framework/e2e/etcd_process.go
+++ b/tests/framework/e2e/etcd_process.go
@@ -105,7 +105,7 @@ type EtcdServerProcessConfig struct {
Proxy *proxy.ServerConfig
}
-func NewEtcdServerProcess(t testing.TB, cfg *EtcdServerProcessConfig) (*EtcdServerProcess, error) {
+func NewEtcdServerProcess(tb testing.TB, cfg *EtcdServerProcessConfig) (*EtcdServerProcess, error) {
if !fileutil.Exist(cfg.ExecPath) {
return nil, fmt.Errorf("could not find etcd binary: %s", cfg.ExecPath)
}
@@ -125,7 +125,7 @@ func NewEtcdServerProcess(t testing.TB, cfg *EtcdServerProcessConfig) (*EtcdServ
}
}
if cfg.LazyFSEnabled {
- ep.lazyfs = newLazyFS(cfg.lg, cfg.DataDirPath, t)
+ ep.lazyfs = newLazyFS(cfg.lg, cfg.DataDirPath, tb)
}
return ep, nil
}
@@ -274,6 +274,9 @@ func (ep *EtcdServerProcess) Logs() LogsExpect {
func (ep *EtcdServerProcess) Kill() error {
ep.cfg.lg.Info("killing server...", zap.String("name", ep.cfg.Name))
+ if ep.proc == nil {
+ return nil
+ }
return ep.proc.Signal(syscall.SIGKILL)
}
@@ -333,7 +336,7 @@ func (ep *EtcdServerProcess) IsRunning() bool {
func AssertProcessLogs(t *testing.T, ep EtcdProcess, expectLog string) {
t.Helper()
var err error
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
_, err = ep.Logs().ExpectWithContext(ctx, expect.ExpectedResponse{Value: expectLog})
if err != nil {
diff --git a/tests/framework/e2e/etcdctl.go b/tests/framework/e2e/etcdctl.go
index 970b67d39d50..237a2b9fcb03 100644
--- a/tests/framework/e2e/etcdctl.go
+++ b/tests/framework/e2e/etcdctl.go
@@ -17,18 +17,15 @@ package e2e
import (
"context"
"encoding/json"
- "errors"
"fmt"
- "io"
"strconv"
"strings"
"time"
- "google.golang.org/grpc"
-
"go.etcd.io/etcd/api/v3/authpb"
"go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/etcdctl/v3/ctlv3/command"
"go.etcd.io/etcd/pkg/v3/expect"
"go.etcd.io/etcd/tests/v3/framework/config"
)
@@ -53,9 +50,9 @@ func NewEtcdctl(cfg ClientConfig, endpoints []string, opts ...config.ClientOptio
client, err := clientv3.New(clientv3.Config{
Endpoints: ctl.endpoints,
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
Username: ctl.authConfig.Username,
Password: ctl.authConfig.Password,
+ Token: ctl.authConfig.Token,
})
if err != nil {
return nil, err
@@ -74,6 +71,13 @@ func WithAuth(userName, password string) config.ClientOption {
}
}
+func WithAuthToken(token string) config.ClientOption {
+ return func(c any) {
+ ctl := c.(*EtcdctlV3)
+ ctl.authConfig.Token = token
+ }
+}
+
func WithEndpoints(endpoints []string) config.ClientOption {
return func(c any) {
ctl := c.(*EtcdctlV3)
@@ -81,6 +85,13 @@ func WithEndpoints(endpoints []string) config.ClientOption {
}
}
+func WithDialTimeout(tio time.Duration) config.ClientOption {
+ return func(c any) {
+ ctl := c.(*EtcdctlV3)
+ ctl.cfg.DialTimeout = tio
+ }
+}
+
func (ctl *EtcdctlV3) DowngradeEnable(ctx context.Context, version string) error {
_, err := SpawnWithExpectLines(ctx, ctl.cmdArgs("downgrade", "enable", version), nil, expect.ExpectedResponse{Value: "Downgrade enable success"})
return err
@@ -92,7 +103,6 @@ func (ctl *EtcdctlV3) DowngradeCancel(ctx context.Context) error {
}
func (ctl *EtcdctlV3) Get(ctx context.Context, key string, o config.GetOptions) (*clientv3.GetResponse, error) {
- resp := clientv3.GetResponse{}
var args []string
if o.Timeout != 0 {
args = append(args, fmt.Sprintf("--command-timeout=%s", o.Timeout))
@@ -116,10 +126,31 @@ func (ctl *EtcdctlV3) Get(ctx context.Context, key string, o config.GetOptions)
if o.FromKey {
args = append(args, "--from-key")
}
+ writeOut := "json"
+ if o.CountOnly || o.KeysOnly {
+ writeOut = "fields"
+ }
+ args = append(args, "-w", writeOut)
if o.CountOnly {
- args = append(args, "-w", "fields", "--count-only")
- } else {
- args = append(args, "-w", "json")
+ args = append(args, "--count-only")
+ }
+ if o.KeysOnly {
+ args = append(args, "--keys-only")
+ }
+ if o.MaxCreateRevision != 0 {
+ args = append(args, fmt.Sprintf("--max-create-rev=%d", o.MaxCreateRevision))
+ }
+ if o.MinCreateRevision != 0 {
+ args = append(args, fmt.Sprintf("--min-create-rev=%d", o.MinCreateRevision))
+ }
+ if o.MaxModRevision != 0 {
+ args = append(args, fmt.Sprintf("--max-mod-rev=%d", o.MaxModRevision))
+ }
+ if o.MinModRevision != 0 {
+ args = append(args, fmt.Sprintf("--min-mod-rev=%d", o.MinModRevision))
+ }
+ if o.Stream {
+ args = append(args, "--stream")
}
switch o.SortBy {
case clientv3.SortByCreateRevision:
@@ -151,15 +182,53 @@ func (ctl *EtcdctlV3) Get(ctx context.Context, key string, o config.GetOptions)
return nil, err
}
defer cmd.Close()
+ // Relying on finding 'Count' as the last line of the output to get all the lines from cmd.Lines()
_, err = cmd.ExpectWithContext(ctx, expect.ExpectedResponse{Value: "Count"})
- return &resp, err
+ if err != nil {
+ return nil, err
+ }
+ return parseFieldsGetResponse(cmd.Lines())
}
+ resp := clientv3.GetResponse{}
err := ctl.spawnJSONCmd(ctx, &resp, args...)
return &resp, err
}
-func (ctl *EtcdctlV3) Put(ctx context.Context, key, value string, opts config.PutOptions) error {
- args := ctl.cmdArgs()
+func parseFieldsGetResponse(lines []string) (*clientv3.GetResponse, error) {
+ resp := &clientv3.GetResponse{Header: &etcdserverpb.ResponseHeader{}}
+ for _, l := range lines {
+ fields := strings.Split(l, ":")
+ key, value := strings.TrimSpace(fields[0]), strings.TrimSpace(fields[1])
+ var err error
+ if key, err = strconv.Unquote(key); err != nil {
+ return resp, err
+ }
+ switch key {
+ case "ClusterID":
+ resp.Header.ClusterId, err = strconv.ParseUint(value, 10, 64)
+ case "MemberID":
+ resp.Header.MemberId, err = strconv.ParseUint(value, 10, 64)
+ case "Revision":
+ resp.Header.Revision, err = strconv.ParseInt(value, 10, 64)
+ case "RaftTerm":
+ resp.Header.RaftTerm, err = strconv.ParseUint(value, 10, 64)
+ case "More":
+ resp.More, err = strconv.ParseBool(value)
+ case "Count":
+ resp.Count, err = strconv.ParseInt(value, 10, 64)
+ default:
+ return resp, fmt.Errorf("unexpected field %q:%s", key, value)
+ }
+ if err != nil {
+ return resp, err
+ }
+ }
+ return resp, nil
+}
+
+func (ctl *EtcdctlV3) Put(ctx context.Context, key, value string, opts config.PutOptions) (*clientv3.PutResponse, error) {
+ resp := clientv3.PutResponse{}
+ args := []string{}
args = append(args, "put", key, value)
if opts.LeaseID != 0 {
args = append(args, "--lease", strconv.FormatInt(int64(opts.LeaseID), 16))
@@ -167,8 +236,8 @@ func (ctl *EtcdctlV3) Put(ctx context.Context, key, value string, opts config.Pu
if opts.Timeout != 0 {
args = append(args, fmt.Sprintf("--command-timeout=%s", opts.Timeout))
}
- _, err := SpawnWithExpectLines(ctx, args, nil, expect.ExpectedResponse{Value: "OK"})
- return err
+ err := ctl.spawnJSONCmd(ctx, &resp, args...)
+ return &resp, err
}
func (ctl *EtcdctlV3) Delete(ctx context.Context, key string, o config.DeleteOptions) (*clientv3.DeleteResponse, error) {
@@ -241,47 +310,11 @@ func (ctl *EtcdctlV3) Txn(ctx context.Context, compares, ifSucess, ifFail []stri
if err != nil {
return nil, err
}
- var resp clientv3.TxnResponse
- AddTxnResponse(&resp, line)
- err = json.Unmarshal([]byte(line), &resp)
- return &resp, err
-}
-
-// AddTxnResponse looks for ResponseOp json tags and adds the objects for json decoding
-func AddTxnResponse(resp *clientv3.TxnResponse, jsonData string) {
- if resp == nil {
- return
- }
- if resp.Responses == nil {
- resp.Responses = []*etcdserverpb.ResponseOp{}
- }
- jd := json.NewDecoder(strings.NewReader(jsonData))
- for {
- t, e := jd.Token()
- if errors.Is(e, io.EOF) {
- break
- }
- if t == "response_range" {
- resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
- Response: &etcdserverpb.ResponseOp_ResponseRange{},
- })
- }
- if t == "response_put" {
- resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
- Response: &etcdserverpb.ResponseOp_ResponsePut{},
- })
- }
- if t == "response_delete_range" {
- resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
- Response: &etcdserverpb.ResponseOp_ResponseDeleteRange{},
- })
- }
- if t == "response_txn" {
- resp.Responses = append(resp.Responses, &etcdserverpb.ResponseOp{
- Response: &etcdserverpb.ResponseOp_ResponseTxn{},
- })
- }
+ var jsonResp command.TxnResponseJSON
+ if err := json.Unmarshal([]byte(line), &jsonResp); err != nil {
+ return nil, err
}
+ return (*clientv3.TxnResponse)(jsonResp.ToProto()), nil
}
func (ctl *EtcdctlV3) MemberList(ctx context.Context, serializable bool) (*clientv3.MemberListResponse, error) {
@@ -350,9 +383,14 @@ func (ctl *EtcdctlV3) flags() map[string]string {
}
}
fmap["endpoints"] = strings.Join(ctl.endpoints, ",")
- if !ctl.authConfig.Empty() {
+ if ctl.authConfig.Token != "" {
+ fmap["auth-jwt-token"] = ctl.authConfig.Token
+ } else if !ctl.authConfig.Empty() {
fmap["user"] = ctl.authConfig.Username + ":" + ctl.authConfig.Password
}
+ if ctl.cfg.DialTimeout != 0 {
+ fmap["dial-timeout"] = ctl.cfg.DialTimeout.String()
+ }
return fmap
}
diff --git a/tests/framework/e2e/e2e_test.go b/tests/framework/e2e/etcdctl_test.go
similarity index 64%
rename from tests/framework/e2e/e2e_test.go
rename to tests/framework/e2e/etcdctl_test.go
index 00059df81ba9..d66ddd584013 100644
--- a/tests/framework/e2e/e2e_test.go
+++ b/tests/framework/e2e/etcdctl_test.go
@@ -16,24 +16,30 @@ package e2e
import (
"encoding/json"
+ "strings"
"testing"
- clientv3 "go.etcd.io/etcd/client/v3"
+ "github.com/stretchr/testify/require"
+
+ "go.etcd.io/etcd/etcdctl/v3/ctlv3/command"
)
-func Test_AddTxnResponse(t *testing.T) {
+func Test_jsonTxnResponse(t *testing.T) {
jsonData := `{"header":{"cluster_id":238453183653593855,"member_id":14578408409545168728,"revision":3,"raft_term":2},"succeeded":true,"responses":[{"Response":{"response_range":{"header":{"revision":3},"kvs":[{"key":"a2V5MQ==","create_revision":2,"mod_revision":2,"version":1,"value":"dmFsdWUx"}],"count":1}}},{"Response":{"response_range":{"header":{"revision":3},"kvs":[{"key":"a2V5Mg==","create_revision":3,"mod_revision":3,"version":1,"value":"dmFsdWUy"}],"count":1}}}]}`
- var resp clientv3.TxnResponse
- AddTxnResponse(&resp, jsonData)
- err := json.Unmarshal([]byte(jsonData), &resp)
- if err != nil {
- t.Errorf("json Unmarshal failed. err: %s", err)
+
+ var jsonTxnResponse command.TxnResponseJSON
+ decoder := json.NewDecoder(strings.NewReader(jsonData))
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(&jsonTxnResponse); err != nil {
+ t.Fatal(err)
}
- enc, err := json.Marshal(resp)
+
+ pb := jsonTxnResponse.ToProto()
+ roundTrippedJSONTxnResponse := command.TxnResponseJSONFromProto(pb)
+ roundTrippedJSONData, err := json.Marshal(roundTrippedJSONTxnResponse)
if err != nil {
- t.Errorf("json Marshal failed. err: %s", err)
- }
- if string(enc) != jsonData {
- t.Error("could not get original message after encoding")
+ t.Fatal(err)
}
+
+ require.JSONEqf(t, jsonData, string(roundTrippedJSONData), "could not get original message after encoding")
}
diff --git a/tests/framework/e2e/metrics.go b/tests/framework/e2e/metrics.go
new file mode 100644
index 000000000000..41137acd7915
--- /dev/null
+++ b/tests/framework/e2e/metrics.go
@@ -0,0 +1,42 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 e2e
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+
+ dto "github.com/prometheus/client_model/go"
+ "github.com/prometheus/common/expfmt"
+ "github.com/prometheus/common/model"
+)
+
+func GetMetrics(metricsURL string) (map[string]*dto.MetricFamily, error) {
+ httpClient := http.Client{Transport: &http.Transport{}}
+ resp, err := httpClient.Get(metricsURL)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ data, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ parser := expfmt.NewTextParser(model.LegacyValidation)
+ return parser.TextToMetricFamilies(bytes.NewReader(data))
+}
diff --git a/tests/framework/e2e/testing.go b/tests/framework/e2e/testing.go
index 7d7de27fdddf..99aa8dac6d65 100644
--- a/tests/framework/e2e/testing.go
+++ b/tests/framework/e2e/testing.go
@@ -20,7 +20,7 @@ import (
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
-func BeforeTest(t testing.TB) {
- SkipInShortMode(t)
- testutil.BeforeTest(t)
+func BeforeTest(tb testing.TB) {
+ SkipInShortMode(tb)
+ testutil.BeforeTest(tb)
}
diff --git a/tests/framework/e2e/util.go b/tests/framework/e2e/util.go
index d72f2d4939d0..94e1cd6a84ab 100644
--- a/tests/framework/e2e/util.go
+++ b/tests/framework/e2e/util.go
@@ -150,8 +150,8 @@ func ToTLS(s string) string {
return s
}
-func SkipInShortMode(t testing.TB) {
- testutil.SkipTestIfShortMode(t, "e2e tests are not running in --short mode")
+func SkipInShortMode(tb testing.TB) {
+ testutil.SkipTestIfShortMode(tb, "e2e tests are not running in --short mode")
}
func mergeEnvVariables(envVars map[string]string) []string {
diff --git a/tests/framework/integration/cluster.go b/tests/framework/integration/cluster.go
index 8b0f2b549528..ccd72b140b91 100644
--- a/tests/framework/integration/cluster.go
+++ b/tests/framework/integration/cluster.go
@@ -17,7 +17,6 @@ package integration
import (
"context"
"crypto/tls"
- "errors"
"fmt"
"io"
"log"
@@ -35,7 +34,6 @@ import (
"time"
"github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
"github.com/soheilhy/cmux"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -43,6 +41,7 @@ import (
"golang.org/x/crypto/bcrypt"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
+ "google.golang.org/protobuf/testing/protocmp"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -139,8 +138,6 @@ type ClusterConfig struct {
PeerTLS *transport.TLSInfo
ClientTLS *transport.TLSInfo
- DiscoveryURL string
-
AuthToken string
QuotaBackendBytes int64
@@ -196,11 +193,6 @@ func SchemeFromTLSInfo(tls *transport.TLSInfo) string {
// fillClusterForMembers fills up Member.InitialPeerURLsMap from each member's [name, scheme and PeerListeners address]
func (c *Cluster) fillClusterForMembers() error {
- if c.Cfg.DiscoveryURL != "" {
- // Cluster will be discovered
- return nil
- }
-
addrs := make([]string, 0)
for _, m := range c.Members {
scheme := SchemeFromTLSInfo(m.PeerTLSInfo)
@@ -296,7 +288,6 @@ func (c *Cluster) MustNewMember(t testutil.TB) *Member {
CorruptCheckTime: c.Cfg.CorruptCheckTime,
Metrics: c.Cfg.Metrics,
})
- m.DiscoveryURL = c.Cfg.DiscoveryURL
return m
}
@@ -395,10 +386,13 @@ func (c *Cluster) WaitMembersMatch(t testutil.TB, membs []*pb.Member) {
default:
}
for {
- resp, err := cc.Cluster.MemberList(ctx, &pb.MemberListRequest{Linearizable: false})
- if errors.Is(err, context.DeadlineExceeded) {
- t.Fatal(err)
+ select {
+ case <-ctx.Done():
+ t.Fatalf("WaitMembersMatch failed: %v", ctx.Err())
+ default:
}
+
+ resp, err := cc.Cluster.MemberList(ctx, &pb.MemberListRequest{Linearizable: false})
if err != nil {
continue
}
@@ -412,23 +406,23 @@ func (c *Cluster) WaitMembersMatch(t testutil.TB, membs []*pb.Member) {
// WaitLeader returns index of the member in c.Members that is leader
// or fails the test (if not established in 30s).
-func (c *Cluster) WaitLeader(t testing.TB) int {
- return c.WaitMembersForLeader(t, c.Members)
+func (c *Cluster) WaitLeader(tb testing.TB) int {
+ return c.WaitMembersForLeader(tb, c.Members)
}
// WaitMembersForLeader waits until given members agree on the same leader,
// and returns its 'index' in the 'membs' list
-func (c *Cluster) WaitMembersForLeader(t testing.TB, membs []*Member) int {
- t.Logf("WaitMembersForLeader")
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+func (c *Cluster) WaitMembersForLeader(tb testing.TB, membs []*Member) int {
+ tb.Logf("WaitMembersForLeader")
+ ctx, cancel := context.WithTimeout(tb.Context(), 30*time.Second)
defer cancel()
l := 0
- for l = c.waitMembersForLeader(ctx, t, membs); l < 0; {
+ for l = c.waitMembersForLeader(ctx, tb, membs); l < 0; {
if ctx.Err() != nil {
- t.Fatalf("WaitLeader FAILED: %v", ctx.Err())
+ tb.Fatalf("WaitLeader FAILED: %v", ctx.Err())
}
}
- t.Logf("WaitMembersForLeader succeeded. Cluster leader index: %v", l)
+ tb.Logf("WaitMembersForLeader succeeded. Cluster leader index: %v", l)
// TODO: Consider second pass check as sometimes leadership is lost
// soon after election:
@@ -444,15 +438,15 @@ func (c *Cluster) WaitMembersForLeader(t testing.TB, membs []*Member) int {
// WaitMembersForLeader waits until given members agree on the same leader,
// and returns its 'index' in the 'membs' list
-func (c *Cluster) waitMembersForLeader(ctx context.Context, t testing.TB, membs []*Member) int {
+func (c *Cluster) waitMembersForLeader(ctx context.Context, tb testing.TB, membs []*Member) int {
possibleLead := make(map[uint64]bool)
var lead uint64
for _, m := range membs {
possibleLead[uint64(m.Server.MemberID())] = true
}
- cc, err := c.ClusterClient(t)
+ cc, err := c.ClusterClient(tb)
if err != nil {
- t.Fatal(err)
+ tb.Fatal(err)
}
// ensure leader is up via linearizable get
for {
@@ -483,12 +477,12 @@ func (c *Cluster) waitMembersForLeader(ctx context.Context, t testing.TB, membs
for i, m := range membs {
if uint64(m.Server.MemberID()) == lead {
- t.Logf("waitMembersForLeader found leader. Member: %v lead: %x", i, lead)
+ tb.Logf("waitMembersForLeader found leader. Member: %v lead: %x", i, lead)
return i
}
}
- t.Logf("waitMembersForLeader failed (-1)")
+ tb.Logf("waitMembersForLeader failed (-1)")
return -1
}
@@ -530,7 +524,9 @@ func (c *Cluster) waitVersion() {
func isMembersEqual(membs []*pb.Member, wmembs []*pb.Member) bool {
sort.Sort(SortableMemberSliceByPeerURLs(membs))
sort.Sort(SortableMemberSliceByPeerURLs(wmembs))
- return cmp.Equal(membs, wmembs, cmpopts.IgnoreFields(pb.Member{}, "ID", "PeerURLs", "ClientURLs"))
+ return cmp.Equal(membs, wmembs,
+ protocmp.Transform(),
+ protocmp.IgnoreFields(&pb.Member{}, "ID"))
}
func NewLocalListener(t testutil.TB) net.Listener {
@@ -734,7 +730,7 @@ func MustNewMember(t testutil.TB, mcfg MemberConfig) *Member {
m.MaxLearners = mcfg.MaxLearners
}
m.Metrics = mcfg.Metrics
- m.V2Deprecation = config.V2_DEPR_DEFAULT
+ m.V2Deprecation = config.V2DeprDefault
m.GRPCServerRecorder = &grpctesting.GRPCRecorder{}
m.Logger, m.LogObserver = memberLogger(t, mcfg.Name)
@@ -905,7 +901,6 @@ func NewClientV3(m *Member) (*clientv3.Client, error) {
cfg := clientv3.Config{
Endpoints: []string{m.GRPCURL},
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
MaxCallSendMsgSize: m.ClientMaxCallSendMsgSize,
MaxCallRecvMsgSize: m.ClientMaxCallRecvMsgSize,
Logger: m.Logger.Named("client"),
@@ -971,7 +966,6 @@ func (m *Member) Launch() error {
if m.Server, err = etcdserver.NewServer(m.ServerConfig); err != nil {
return fmt.Errorf("failed to initialize the etcd server: %w", err)
}
- m.Server.SyncTicker = time.NewTicker(500 * time.Millisecond)
m.Server.Start()
var peerTLScfg *tls.Config
@@ -1457,7 +1451,7 @@ func (c *Cluster) Endpoints() []string {
return endpoints
}
-func (c *Cluster) ClusterClient(t testing.TB, opts ...framecfg.ClientOption) (client *clientv3.Client, err error) {
+func (c *Cluster) ClusterClient(tb testing.TB, opts ...framecfg.ClientOption) (client *clientv3.Client, err error) {
cfg, err := c.newClientCfg()
if err != nil {
return nil, err
@@ -1469,7 +1463,7 @@ func (c *Cluster) ClusterClient(t testing.TB, opts ...framecfg.ClientOption) (cl
if err != nil {
return nil, err
}
- t.Cleanup(func() {
+ tb.Cleanup(func() {
client.Close()
})
return client, nil
@@ -1483,6 +1477,13 @@ func WithAuth(userName, password string) framecfg.ClientOption {
}
}
+func WithAuthToken(token string) framecfg.ClientOption {
+ return func(c any) {
+ cfg := c.(*clientv3.Config)
+ cfg.Token = token
+ }
+}
+
func WithEndpoints(endpoints []string) framecfg.ClientOption {
return func(c any) {
cfg := c.(*clientv3.Config)
@@ -1490,11 +1491,17 @@ func WithEndpoints(endpoints []string) framecfg.ClientOption {
}
}
+func WithDialTimeout(tio time.Duration) framecfg.ClientOption {
+ return func(c any) {
+ cfg := c.(*clientv3.Config)
+ cfg.DialTimeout = tio
+ }
+}
+
func (c *Cluster) newClientCfg() (*clientv3.Config, error) {
cfg := &clientv3.Config{
Endpoints: c.Endpoints(),
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
MaxCallSendMsgSize: c.Cfg.ClientMaxCallSendMsgSize,
MaxCallRecvMsgSize: c.Cfg.ClientMaxCallRecvMsgSize,
}
diff --git a/tests/framework/integration/config.go b/tests/framework/integration/config.go
new file mode 100644
index 000000000000..c969f1ca7495
--- /dev/null
+++ b/tests/framework/integration/config.go
@@ -0,0 +1,49 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 integration
+
+import "go.etcd.io/etcd/tests/v3/framework/config"
+
+type ClusterContext struct {
+ UseUnix bool
+}
+
+func WithHTTP2Debug() config.ClusterOption {
+ return func(c *config.ClusterConfig) {}
+}
+
+func WithUnixClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {
+ ctx := ensureIntegrationClusterContext(c)
+ ctx.UseUnix = true
+ c.ClusterContext = ctx
+ }
+}
+
+func WithTCPClient() config.ClusterOption {
+ return func(c *config.ClusterConfig) {
+ ctx := ensureIntegrationClusterContext(c)
+ ctx.UseUnix = false
+ c.ClusterContext = ctx
+ }
+}
+
+func ensureIntegrationClusterContext(c *config.ClusterConfig) *ClusterContext {
+ ctx, _ := c.ClusterContext.(*ClusterContext)
+ if ctx == nil {
+ ctx = &ClusterContext{}
+ }
+ return ctx
+}
diff --git a/tests/framework/integration/integration.go b/tests/framework/integration/integration.go
index 6e5de0cd5281..28944b590fbf 100644
--- a/tests/framework/integration/integration.go
+++ b/tests/framework/integration/integration.go
@@ -41,11 +41,11 @@ func (e integrationRunner) TestMain(m *testing.M) {
testutil.MustTestMainWithLeakDetection(m)
}
-func (e integrationRunner) BeforeTest(t testing.TB) {
- BeforeTest(t)
+func (e integrationRunner) BeforeTest(tb testing.TB) {
+ BeforeTest(tb)
}
-func (e integrationRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.ClusterOption) intf.Cluster {
+func (e integrationRunner) NewCluster(ctx context.Context, tb testing.TB, opts ...config.ClusterOption) intf.Cluster {
var err error
cfg := config.NewClusterConfig(opts...)
integrationCfg := ClusterConfig{
@@ -55,27 +55,35 @@ func (e integrationRunner) NewCluster(ctx context.Context, t testing.TB, opts ..
AuthToken: cfg.AuthToken,
SnapshotCount: cfg.SnapshotCount,
}
- integrationCfg.ClientTLS, err = tlsInfo(t, cfg.ClientTLS)
+ integrationCfg.ClientTLS, err = tlsInfo(tb, cfg.ClientTLS)
if err != nil {
- t.Fatalf("ClientTLS: %s", err)
+ tb.Fatalf("ClientTLS: %s", err)
}
- integrationCfg.PeerTLS, err = tlsInfo(t, cfg.PeerTLS)
+ integrationCfg.PeerTLS, err = tlsInfo(tb, cfg.PeerTLS)
if err != nil {
- t.Fatalf("PeerTLS: %s", err)
+ tb.Fatalf("PeerTLS: %s", err)
}
+
+ if cfg.ClusterContext != nil {
+ if ctx, ok := cfg.ClusterContext.(*ClusterContext); ok && ctx != nil {
+ integrationCfg.UseTCP = !ctx.UseUnix
+ integrationCfg.UseIP = !ctx.UseUnix
+ }
+ }
+
return &integrationCluster{
- Cluster: NewCluster(t, &integrationCfg),
- t: t,
+ Cluster: NewCluster(tb, &integrationCfg),
+ t: tb,
ctx: ctx,
}
}
-func tlsInfo(t testing.TB, cfg config.TLSConfig) (*transport.TLSInfo, error) {
+func tlsInfo(tb testing.TB, cfg config.TLSConfig) (*transport.TLSInfo, error) {
switch cfg {
case config.NoTLS:
return nil, nil
case config.AutoTLS:
- tls, err := transport.SelfCert(zap.NewNop(), t.TempDir(), []string{"localhost"}, 1)
+ tls, err := transport.SelfCert(zap.NewNop(), tb.TempDir(), []string{"localhost"}, 1)
if err != nil {
return nil, fmt.Errorf("failed to generate cert: %w", err)
}
@@ -100,6 +108,48 @@ func (c *integrationCluster) Members() (ms []intf.Member) {
return ms
}
+func (c *integrationCluster) TemplateEndpoints(tb testing.TB, pattern string) []string {
+ tb.Helper()
+ var endpoints []string
+ for _, m := range c.Cluster.Members {
+ ent := pattern
+ ent = strings.ReplaceAll(ent, "${MEMBER_PORT}", m.GRPCPortNumber())
+ ent = strings.ReplaceAll(ent, "${MEMBER_NAME}", m.Name)
+ endpoints = append(endpoints, ent)
+ }
+ return endpoints
+}
+
+func templateAuthority(tb testing.TB, pattern string, m *Member) string {
+ tb.Helper()
+ authority := pattern
+ authority = strings.ReplaceAll(authority, "${MEMBER_PORT}", m.GRPCPortNumber())
+ authority = strings.ReplaceAll(authority, "${MEMBER_NAME}", m.Name)
+ return authority
+}
+
+func (c *integrationCluster) AssertAuthority(tb testing.TB, expectedAuthorityPattern string) {
+ tb.Helper()
+ const filterMethod = "/etcdserverpb.KV/Put"
+ for _, m := range c.Cluster.Members {
+ expectedAuthority := templateAuthority(tb, expectedAuthorityPattern, m)
+ requestsFound := 0
+ for _, r := range m.RecordedRequests() {
+ if r.FullMethod != filterMethod {
+ continue
+ }
+ if r.Authority == expectedAuthority {
+ requestsFound++
+ } else {
+ tb.Errorf("Got unexpected authority header, member %q, request %q, got %q, expected %q", m.Name, r.FullMethod, r.Authority, expectedAuthority)
+ }
+ }
+ if requestsFound == 0 {
+ tb.Errorf("Expect at least one request with matched authority header value was recorded by the server intercepter on member %s but got 0", m.Name)
+ }
+ }
+}
+
type integrationMember struct {
*Member
t testing.TB
@@ -162,13 +212,35 @@ func (c integrationClient) Get(ctx context.Context, key string, o config.GetOpti
if o.CountOnly {
clientOpts = append(clientOpts, clientv3.WithCountOnly())
}
+ if o.KeysOnly {
+ clientOpts = append(clientOpts, clientv3.WithKeysOnly())
+ }
if o.SortBy != clientv3.SortByKey || o.Order != clientv3.SortNone {
clientOpts = append(clientOpts, clientv3.WithSort(o.SortBy, o.Order))
}
+ if o.MaxCreateRevision != 0 {
+ clientOpts = append(clientOpts, clientv3.WithMaxCreateRev(int64(o.MaxCreateRevision)))
+ }
+ if o.MinCreateRevision != 0 {
+ clientOpts = append(clientOpts, clientv3.WithMinCreateRev(int64(o.MinCreateRevision)))
+ }
+ if o.MaxModRevision != 0 {
+ clientOpts = append(clientOpts, clientv3.WithMaxModRev(int64(o.MaxModRevision)))
+ }
+ if o.MinModRevision != 0 {
+ clientOpts = append(clientOpts, clientv3.WithMinModRev(int64(o.MinModRevision)))
+ }
+ if o.Stream {
+ stream, err := c.Client.GetStream(ctx, key, clientOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return clientv3.GetStreamToGetResponse(stream)
+ }
return c.Client.Get(ctx, key, clientOpts...)
}
-func (c integrationClient) Put(ctx context.Context, key, value string, opts config.PutOptions) error {
+func (c integrationClient) Put(ctx context.Context, key, value string, opts config.PutOptions) (*clientv3.PutResponse, error) {
if opts.Timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, opts.Timeout)
@@ -178,8 +250,7 @@ func (c integrationClient) Put(ctx context.Context, key, value string, opts conf
if opts.LeaseID != 0 {
clientOpts = append(clientOpts, clientv3.WithLease(opts.LeaseID))
}
- _, err := c.Client.Put(ctx, key, value, clientOpts...)
- return err
+ return c.Client.Put(ctx, key, value, clientOpts...)
}
func (c integrationClient) Delete(ctx context.Context, key string, o config.DeleteOptions) (*clientv3.DeleteResponse, error) {
diff --git a/tests/framework/integration/testing.go b/tests/framework/integration/testing.go
index a4d03c532123..7a6fde90ae1e 100644
--- a/tests/framework/integration/testing.go
+++ b/tests/framework/integration/testing.go
@@ -18,10 +18,8 @@ import (
"os"
"testing"
- grpclogsettable "github.com/grpc-ecosystem/go-grpc-middleware/logging/settable"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
- "go.uber.org/zap/zapgrpc"
"go.uber.org/zap/zaptest"
"go.etcd.io/etcd/client/pkg/v3/testutil"
@@ -32,14 +30,9 @@ import (
)
var (
- grpcLogger grpclogsettable.SettableLoggerV2
insideTestContext bool
)
-func init() {
- grpcLogger = grpclogsettable.ReplaceGrpcLoggerV2()
-}
-
type testOptions struct {
goLeakDetection bool
skipInShort bool
@@ -83,6 +76,12 @@ func BeforeTestExternal(t testutil.TB) {
BeforeTest(t, WithoutSkipInShort(), WithoutGoLeakDetection())
}
+func SkipIfNoGoFail(t testutil.TB) {
+ if len(gofail.List()) == 0 {
+ t.Skip("please run 'make gofail-enable' before running the test")
+ }
+}
+
func BeforeTest(t testutil.TB, opts ...TestOption) {
t.Helper()
options := newTestOptions(opts...)
@@ -100,9 +99,7 @@ func BeforeTest(t testutil.TB, opts ...TestOption) {
}
if options.failpoint != nil && len(options.failpoint.name) != 0 {
- if len(gofail.List()) == 0 {
- t.Skip("please run 'make gofail-enable' before running the test")
- }
+ SkipIfNoGoFail(t)
require.NoError(t, gofail.Enable(options.failpoint.name, options.failpoint.payload))
t.Cleanup(func() {
require.NoError(t, gofail.Disable(options.failpoint.name))
@@ -120,13 +117,11 @@ func BeforeTest(t testutil.TB, opts ...TestOption) {
// Registering cleanup early, such it will get executed even if the helper fails.
t.Cleanup(func() {
- grpcLogger.Reset()
insideTestContext = previousInsideTestContext
os.Chdir(previousWD)
revertFunc()
})
- grpcLogger.Set(zapgrpc.NewLogger(zaptest.NewLogger(t).Named("grpc")))
insideTestContext = true
os.Chdir(t.TempDir())
@@ -138,18 +133,18 @@ func assertInTestContext(t testutil.TB) {
}
}
-func NewEmbedConfig(t testing.TB, name string) *embed.Config {
+func NewEmbedConfig(tb testing.TB, name string) *embed.Config {
cfg := embed.NewConfig()
cfg.Name = name
- lg := zaptest.NewLogger(t, zaptest.Level(zapcore.InfoLevel)).Named(cfg.Name)
+ lg := zaptest.NewLogger(tb, zaptest.Level(zapcore.InfoLevel)).Named(cfg.Name)
cfg.ZapLoggerBuilder = embed.NewZapLoggerBuilder(lg)
- cfg.Dir = t.TempDir()
+ cfg.Dir = tb.TempDir()
return cfg
}
-func NewClient(t testing.TB, cfg clientv3.Config) (*clientv3.Client, error) {
+func NewClient(tb testing.TB, cfg clientv3.Config) (*clientv3.Client, error) {
if cfg.Logger == nil {
- cfg.Logger = zaptest.NewLogger(t).Named("client")
+ cfg.Logger = zaptest.NewLogger(tb).Named("client")
}
return clientv3.New(cfg)
}
diff --git a/tests/framework/interfaces/interface.go b/tests/framework/interfaces/interface.go
index 0477ea5f0ec7..f69e1bc7f308 100644
--- a/tests/framework/interfaces/interface.go
+++ b/tests/framework/interfaces/interface.go
@@ -43,7 +43,7 @@ type Member interface {
}
type Client interface {
- Put(context context.Context, key, value string, opts config.PutOptions) error
+ Put(context context.Context, key, value string, opts config.PutOptions) (*clientv3.PutResponse, error)
Get(context context.Context, key string, opts config.GetOptions) (*clientv3.GetResponse, error)
Delete(context context.Context, key string, opts config.DeleteOptions) (*clientv3.DeleteResponse, error)
Compact(context context.Context, rev int64, opts config.CompactOption) (*clientv3.CompactResponse, error)
@@ -85,3 +85,11 @@ type Client interface {
Watch(ctx context.Context, key string, opts config.WatchOptions) clientv3.WatchChan
}
+
+type TemplateEndpoints interface {
+ TemplateEndpoints(tb testing.TB, pattern string) []string
+}
+
+type AssertAuthority interface {
+ AssertAuthority(tb testing.TB, expectedAuthorityPattern string)
+}
diff --git a/tests/framework/testutils/execute.go b/tests/framework/testutils/execute.go
index d9c3d3358796..9ea7e1222243 100644
--- a/tests/framework/testutils/execute.go
+++ b/tests/framework/testutils/execute.go
@@ -24,7 +24,7 @@ import (
)
func ExecuteWithTimeout(t *testing.T, timeout time.Duration, f func()) {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
ExecuteUntil(ctx, t, f)
}
diff --git a/tests/framework/testutils/log_observer_test.go b/tests/framework/testutils/log_observer_test.go
index 695caf97261d..a11ee175c7a2 100644
--- a/tests/framework/testutils/log_observer_test.go
+++ b/tests/framework/testutils/log_observer_test.go
@@ -32,7 +32,7 @@ func TestLogObserver_Timeout(t *testing.T) {
logger := zap.New(logCore)
logger.Info(t.Name())
- ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
_, err := logOb.Expect(ctx, "unknown", 1)
cancel()
require.ErrorIs(t, err, context.DeadlineExceeded)
@@ -45,7 +45,7 @@ func TestLogObserver_Expect(t *testing.T) {
logger := zap.New(logCore)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
resCh := make(chan []string, 1)
diff --git a/tests/framework/unit/unit.go b/tests/framework/unit/unit.go
index f822b7dd1f92..d09ef0bdd2be 100644
--- a/tests/framework/unit/unit.go
+++ b/tests/framework/unit/unit.go
@@ -42,10 +42,10 @@ func (e unitRunner) TestMain(m *testing.M) {
}
}
-func (e unitRunner) BeforeTest(t testing.TB) {
+func (e unitRunner) BeforeTest(tb testing.TB) {
}
-func (e unitRunner) NewCluster(ctx context.Context, t testing.TB, opts ...config.ClusterOption) intf.Cluster {
- testutil.SkipTestIfShortMode(t, "Cannot create clusters in --short tests")
+func (e unitRunner) NewCluster(ctx context.Context, tb testing.TB, opts ...config.ClusterOption) intf.Cluster {
+ testutil.SkipTestIfShortMode(tb, "Cannot create clusters in --short tests")
return nil
}
diff --git a/tests/go.mod b/tests/go.mod
index 8a8eb162437c..c06f9affa5e1 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -1,11 +1,12 @@
module go.etcd.io/etcd/tests/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
replace (
go.etcd.io/etcd/api/v3 => ../api
+ go.etcd.io/etcd/cache/v3 => ../cache
go.etcd.io/etcd/client/pkg/v3 => ../client/pkg
go.etcd.io/etcd/client/v3 => ../client/v3
go.etcd.io/etcd/etcdctl/v3 => ../etcdctl
@@ -15,94 +16,92 @@ replace (
)
require (
- github.com/anishathalye/porcupine v0.1.4
+ github.com/anishathalye/porcupine v1.1.0
+ github.com/antithesishq/antithesis-sdk-go v0.4.3
github.com/coreos/go-semver v0.3.1
+ github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
- github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
- github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
- github.com/prometheus/client_golang v1.20.5
- github.com/prometheus/client_model v0.6.1
- github.com/prometheus/common v0.62.0
+ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
+ github.com/olekukonko/tablewriter v1.1.4
+ github.com/prometheus/client_golang v1.23.2
+ github.com/prometheus/client_model v0.6.2
+ github.com/prometheus/common v0.67.5
github.com/soheilhy/cmux v0.1.5
github.com/stretchr/testify v1.11.1
- go.etcd.io/bbolt v1.4.3
- go.etcd.io/etcd/api/v3 v3.6.10
- go.etcd.io/etcd/client/pkg/v3 v3.6.10
- go.etcd.io/etcd/client/v2 v2.305.20
- go.etcd.io/etcd/client/v3 v3.6.10
- go.etcd.io/etcd/etcdctl/v3 v3.6.10
- go.etcd.io/etcd/etcdutl/v3 v3.6.10
- go.etcd.io/etcd/pkg/v3 v3.6.10
- go.etcd.io/etcd/server/v3 v3.6.10
+ go.etcd.io/bbolt v1.5.0
+ go.etcd.io/etcd/api/v3 v3.7.0
+ go.etcd.io/etcd/cache/v3 v3.7.0
+ go.etcd.io/etcd/client/pkg/v3 v3.7.0
+ go.etcd.io/etcd/client/v3 v3.7.0
+ go.etcd.io/etcd/etcdctl/v3 v3.7.0
+ go.etcd.io/etcd/etcdutl/v3 v3.7.0
+ go.etcd.io/etcd/pkg/v3 v3.7.0
+ go.etcd.io/etcd/server/v3 v3.7.0
go.etcd.io/gofail v0.2.0
- go.etcd.io/raft/v3 v3.6.0
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0
- go.opentelemetry.io/otel v1.40.0
- go.opentelemetry.io/otel/sdk v1.40.0
- go.opentelemetry.io/otel/trace v1.40.0
- go.opentelemetry.io/proto/otlp v1.5.0
- go.uber.org/zap v1.27.0
- golang.org/x/crypto v0.48.0
+ go.etcd.io/raft/v3 v3.7.0
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
+ go.opentelemetry.io/otel v1.43.0
+ go.opentelemetry.io/otel/sdk v1.43.0
+ go.opentelemetry.io/proto/otlp v1.10.0
+ go.uber.org/zap v1.27.1
+ golang.org/x/crypto v0.52.0
golang.org/x/sync v0.20.0
- golang.org/x/time v0.9.0
- google.golang.org/grpc v1.79.3
- google.golang.org/protobuf v1.36.10
+ golang.org/x/time v0.15.0
+ google.golang.org/grpc v1.81.0
+ google.golang.org/protobuf v1.36.11
)
require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.2.0 // indirect
- github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/cheggaaa/pb/v3 v3.1.6 // indirect
- github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+ github.com/cheggaaa/pb/v3 v3.1.7 // indirect
+ github.com/clipperhouse/displaywidth v0.11.0 // indirect
+ github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/creack/pty v1.1.18 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/fatih/color v1.18.0 // indirect
+ github.com/fatih/color v1.19.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
- github.com/google/btree v1.1.3 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
- github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
- github.com/json-iterator/go v1.1.12 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
- github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
- github.com/olekukonko/tablewriter v0.0.5 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
+ github.com/olekukonko/errors v1.2.0 // indirect
+ github.com/olekukonko/ll v0.1.6 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/sirupsen/logrus v1.9.4 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
- github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
+ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
+ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
- go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
- go.opentelemetry.io/otel/metric v1.40.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
+ go.opentelemetry.io/otel/metric v1.43.0 // indirect
+ go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect
- sigs.k8s.io/yaml v1.4.0 // indirect
+ k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/tests/go.sum b/tests/go.sum
index f52923767567..74db1a045df8 100644
--- a/tests/go.sum
+++ b/tests/go.sum
@@ -1,95 +1,68 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
-github.com/anishathalye/porcupine v0.1.4 h1:rRekB2jH1mbtLPEzuqyMHp4scU52Bcc1jgkPi1kWFQA=
-github.com/anishathalye/porcupine v0.1.4/go.mod h1:/X9OQYnVb7DzfKCQVO4tI1Aq+o56UJW+RvN/5U4EuZA=
+github.com/anishathalye/porcupine v1.1.0 h1:jkMLqDejaWqvhvjxYKyqwQO3d1Jw+/08wHiIw0O4wcU=
+github.com/anishathalye/porcupine v1.1.0/go.mod h1:WM0SsFjWNl2Y4BqHr/E/ll2yY1GY1jqn+W7Z/84Zoog=
+github.com/antithesishq/antithesis-sdk-go v0.4.3 h1:a2hGdDogClzHzFu20r1z0tzD6zwSWUipiaerAjZVP90=
+github.com/antithesishq/antithesis-sdk-go v0.4.3/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl3v2yvUZjmKncl7U91fup7E=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
-github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
+github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
-github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
+github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
-github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
+github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
+github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
-github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
-github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
-github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
+github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns=
+github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
-github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -100,40 +73,35 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
+github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
+github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
+github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
+github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
+github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
+github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
+github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
+github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
@@ -142,146 +110,99 @@ github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
-go.etcd.io/etcd/client/v2 v2.305.20 h1:3nOyi6p2NQsgPIIXXEhxYEvyW62h94Q0DsMiKHeApvE=
-go.etcd.io/etcd/client/v2 v2.305.20/go.mod h1:elQUPzRTJKHQn4nGYlempVfkKEd+zxIEHDoW0OaudYs=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
+github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
+go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA=
go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o=
-go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
-go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
+go.etcd.io/raft/v3 v3.7.0 h1:BGzlwx07bLv8PW6OU5HObuz1y4hlPZUXA07pM1mPUh4=
+go.etcd.io/raft/v3 v3.7.0/go.mod h1:6gX6T2X907DjnjsFLODnTxba77stjs84W9gTTI0GUNA=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
-go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
-go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
-go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
-go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
-go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
-go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
-go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
-go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
-go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
-go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
-go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
+go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
+go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=
-sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
-sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY=
+k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/tests/integration/cache_test.go b/tests/integration/cache_test.go
new file mode 100644
index 000000000000..019daf8764ca
--- /dev/null
+++ b/tests/integration/cache_test.go
@@ -0,0 +1,1716 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 integration
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/rand"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/testing/protocmp"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ "go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
+ cache "go.etcd.io/etcd/cache/v3"
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/pkg/v3/stringutil"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
+)
+
+func TestCacheWithoutPrefixWatch(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ c, err := cache.New(client, "", cache.WithHistoryWindowSize(32))
+ if err != nil {
+ t.Fatalf("New(...): %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(t.Context()); err != nil {
+ t.Fatalf("cache not ready: %v", err)
+ }
+ testWatch(t, client.KV, c)
+}
+
+func TestWatch(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ testWatch(t, client.KV, client.Watcher)
+}
+
+func testWatch(t *testing.T, kv clientv3.KV, watcher Watcher) {
+ ctx := t.Context()
+ rev2PutFooA := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("1"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ rev3PutFooB := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/b"),
+ Value: []byte("2"),
+ CreateRevision: 3,
+ ModRevision: 3,
+ Version: 1,
+ },
+ }
+ rev4DeleteFooA := &clientv3.Event{
+ Type: clientv3.EventTypeDelete,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ ModRevision: 4,
+ },
+ }
+ rev5PutFooA := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("3"),
+ CreateRevision: 5,
+ ModRevision: 5,
+ Version: 1,
+ },
+ }
+ rev5DeleteFooB := &clientv3.Event{
+ Type: clientv3.EventTypeDelete,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/b"),
+ ModRevision: 5,
+ },
+ }
+ rev6PutFooC := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/c"),
+ Value: []byte("x"),
+ CreateRevision: 6,
+ ModRevision: 6,
+ Version: 1,
+ },
+ }
+ rev7PutFooBar := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/bar"),
+ Value: []byte("y"),
+ CreateRevision: 7,
+ ModRevision: 7,
+ Version: 1,
+ },
+ }
+ rev8PutFooBaz := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/baz"),
+ Value: []byte("z"),
+ CreateRevision: 8,
+ ModRevision: 8,
+ Version: 1,
+ },
+ }
+ rev9PutFooYoo := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/yoo"),
+ Value: []byte("1"),
+ CreateRevision: 9,
+ ModRevision: 9,
+ Version: 1,
+ },
+ }
+ rev10PutZoo := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/zoo"),
+ Value: []byte("1"),
+ CreateRevision: 10,
+ ModRevision: 10,
+ Version: 1,
+ },
+ }
+ rev11PutFooFuture := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/future"),
+ Value: []byte("42"),
+ CreateRevision: 11,
+ ModRevision: 11,
+ Version: 1,
+ },
+ }
+ rev12PutFooTx1 := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/tx1"),
+ Value: []byte("a"),
+ CreateRevision: 12,
+ ModRevision: 12,
+ Version: 1,
+ },
+ }
+ rev12DeleteFooFuture := &clientv3.Event{
+ Type: clientv3.EventTypeDelete,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/future"),
+ ModRevision: 12,
+ },
+ }
+ rev12PutFooTx2 := &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/tx2"),
+ Value: []byte("b"),
+ CreateRevision: 12,
+ ModRevision: 12,
+ Version: 1,
+ },
+ }
+
+ tcs := []struct {
+ name string
+ key string
+ opts []clientv3.OpOption
+ wantEvents []*clientv3.Event
+ }{
+ {
+ name: "Watch single key existing /foo/c",
+ key: "/foo/c",
+ opts: []clientv3.OpOption{clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev6PutFooC},
+ },
+ {
+ name: "Watch single key non‑existent /doesnotexist",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithRev(2)},
+ wantEvents: nil,
+ },
+ {
+ name: "Watch range empty",
+ key: "",
+ opts: []clientv3.OpOption{clientv3.WithRange(""), clientv3.WithRev(2)},
+ wantEvents: nil,
+ },
+ {
+ name: "Watch range [/foo/a, /foo/b)",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/b"), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev2PutFooA, rev4DeleteFooA, rev5PutFooA},
+ },
+ {
+ name: "Watch with prefix /foo/b",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev3PutFooB, rev5DeleteFooB, rev7PutFooBar, rev8PutFooBaz},
+ },
+ {
+ name: "Watch with prefix non-existent /doesnotexist",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(2)},
+ wantEvents: nil,
+ },
+ {
+ name: "Watch with prefix empty string",
+ key: "",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev2PutFooA, rev3PutFooB, rev4DeleteFooA, rev5PutFooA, rev5DeleteFooB, rev6PutFooC, rev7PutFooBar, rev8PutFooBaz, rev9PutFooYoo, rev10PutZoo, rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from key /foo/b",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev3PutFooB, rev5DeleteFooB, rev6PutFooC, rev7PutFooBar, rev8PutFooBaz, rev9PutFooYoo, rev10PutZoo, rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from empty key",
+ key: "",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev2PutFooA, rev3PutFooB, rev4DeleteFooA, rev5PutFooA, rev5DeleteFooB, rev6PutFooC, rev7PutFooBar, rev8PutFooBaz, rev9PutFooYoo, rev10PutZoo, rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from non-existent key /doesnotexist",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithRev(2)},
+ wantEvents: []*clientv3.Event{rev2PutFooA, rev3PutFooB, rev4DeleteFooA, rev5PutFooA, rev5DeleteFooB, rev6PutFooC, rev7PutFooBar, rev8PutFooBaz, rev9PutFooYoo, rev10PutZoo, rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from rev 4 with single key /foo/a",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(4)},
+ wantEvents: []*clientv3.Event{rev4DeleteFooA, rev5PutFooA},
+ },
+ {
+ name: "Watch from rev 6 with single key /foo/a",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(6)},
+ wantEvents: nil,
+ },
+ {
+ name: "Watch from rev 5 with prefix /foo",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(5)},
+ wantEvents: []*clientv3.Event{
+ rev5PutFooA, rev5DeleteFooB, rev6PutFooC, rev7PutFooBar, rev8PutFooBaz, rev9PutFooYoo, rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2,
+ },
+ },
+ {
+ name: "Watch from rev 10 with prefix /foo",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(10)},
+ wantEvents: []*clientv3.Event{
+ rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2,
+ },
+ },
+ {
+ name: "Watch from rev 4 with range [/foo/a, /foo/c)",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/c"), clientv3.WithRev(4)},
+ wantEvents: []*clientv3.Event{
+ rev4DeleteFooA, rev5PutFooA, rev5DeleteFooB, rev7PutFooBar, rev8PutFooBaz,
+ },
+ },
+ {
+ name: "Latest‑revision watcher for /foo",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ wantEvents: []*clientv3.Event{rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from rev 11 with single key /foo/future",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithRev(11), clientv3.WithPrefix()},
+ wantEvents: []*clientv3.Event{rev11PutFooFuture, rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ {
+ name: "Watch from rev 12 with txn prefix /foo",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithRev(12), clientv3.WithPrefix()},
+ wantEvents: []*clientv3.Event{rev12PutFooTx1, rev12DeleteFooFuture, rev12PutFooTx2},
+ },
+ }
+
+ t.Log("Write the first batch of events rev 2-10")
+ if _, err := kv.Put(ctx, string(rev2PutFooA.Kv.Key), string(rev2PutFooA.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev3PutFooB.Kv.Key), string(rev3PutFooB.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Delete(ctx, string(rev4DeleteFooA.Kv.Key)); err != nil {
+ t.Fatalf("Delete: %v", err)
+ }
+ if _, err := kv.Txn(ctx).Then(clientv3.OpPut(string(rev5PutFooA.Kv.Key), string(rev5PutFooA.Kv.Value)), clientv3.OpDelete(string(rev5DeleteFooB.Kv.Key))).Commit(); err != nil {
+ t.Fatalf("Txn: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev6PutFooC.Kv.Key), string(rev6PutFooC.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev7PutFooBar.Kv.Key), string(rev7PutFooBar.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev8PutFooBaz.Kv.Key), string(rev8PutFooBaz.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev9PutFooYoo.Kv.Key), string(rev9PutFooYoo.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ if _, err := kv.Put(ctx, string(rev10PutZoo.Kv.Key), string(rev10PutZoo.Kv.Value)); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+
+ t.Log("Open watches")
+ watches := make([]clientv3.WatchChan, len(tcs))
+ for i, tc := range tcs {
+ watches[i] = watcher.Watch(ctx, tc.key, tc.opts...)
+ }
+ time.Sleep(50 * time.Millisecond)
+
+ t.Log("Write the second batch of events rev 11‑12")
+ if _, err := kv.Put(ctx, string(rev11PutFooFuture.Kv.Key), string(rev11PutFooFuture.Kv.Value)); err != nil {
+ t.Fatalf("Put /foo/future: %v", err)
+ }
+ if _, err := kv.Txn(ctx).Then(
+ clientv3.OpPut(string(rev12PutFooTx1.Kv.Key), string(rev12PutFooTx1.Kv.Value)),
+ clientv3.OpDelete(string(rev12DeleteFooFuture.Kv.Key)),
+ clientv3.OpPut(string(rev12PutFooTx2.Kv.Key), string(rev12PutFooTx2.Kv.Value)),
+ ).Commit(); err != nil {
+ t.Fatalf("Txn rev12: %v", err)
+ }
+
+ t.Log("Validate")
+ for i, tc := range tcs {
+ i, tc := i, tc
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+ events, _ := collectAndAssertAtomicEvents(t, watches[i])
+ if diff := cmp.Diff(tc.wantEvents, events, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected events (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestCacheWithPrefixWatch(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ ctx := t.Context()
+
+ tests := []struct {
+ name string
+ key string
+ opts []clientv3.OpOption
+ expectCanceled bool
+ }{
+ {
+ name: "single key within prefix",
+ key: "/foo/a",
+ opts: nil,
+ expectCanceled: false,
+ },
+ {
+ name: "single key outside prefix returns error",
+ key: "/bar/a",
+ opts: nil,
+ expectCanceled: true,
+ },
+ {
+ name: "prefix() within cache prefix",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ expectCanceled: false,
+ },
+ {
+ name: "prefix() outside cache prefix returns error",
+ key: "/bar",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ expectCanceled: true,
+ },
+ {
+ name: "range within prefix",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/b")},
+ expectCanceled: false,
+ },
+ {
+ name: "range crosses cache prefix boundary returns error",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/zzz")},
+ expectCanceled: true,
+ },
+ {
+ name: "fromKey not allowed when cache has prefix returns error",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ expectCanceled: true,
+ },
+ }
+
+ const testPutKey = "/foo/a"
+
+ for _, tc := range tests {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ c, err := cache.New(client, "/foo")
+ if err != nil {
+ t.Fatalf("New(...): %v", err)
+ }
+ defer c.Close()
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatal(err)
+ }
+
+ watchCtx, cancel := context.WithTimeout(ctx, time.Second)
+ defer cancel()
+
+ ch := c.Watch(watchCtx, tc.key, tc.opts...)
+
+ if !tc.expectCanceled {
+ if _, err := client.Put(ctx, testPutKey, "val"); err != nil {
+ t.Fatalf("Put(%q): %v", testPutKey, err)
+ }
+ }
+
+ select {
+ case resp, ok := <-ch:
+ if tc.expectCanceled {
+ if !ok || !resp.Canceled {
+ t.Fatalf("expected canceled watch, got %+v (closed=%v)", resp, !ok)
+ }
+ return
+ }
+
+ if !ok || resp.Canceled {
+ t.Fatalf("expected active watch (not canceled), got %+v (closed=%v)", resp, !ok)
+ }
+ if len(resp.Events) == 0 {
+ t.Fatalf("watch returned no events, expected at least the test event")
+ }
+ if string(resp.Events[0].Kv.Key) != testPutKey {
+ t.Fatalf("got event for key %q, want %q", resp.Events[0].Kv.Key, testPutKey)
+ }
+ case <-watchCtx.Done():
+ if tc.expectCanceled {
+ t.Fatalf("watch did not cancel within timeout")
+ } else {
+ t.Fatalf("active watch did not deliver event within timeout")
+ }
+ }
+ })
+ }
+}
+
+func TestCacheServerRequestProgress(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("grpc proxy currently does not support requesting progress notifications")
+ }
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ ctx := t.Context()
+
+ c, err := cache.New(client, "/foo")
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatalf("cache.WaitReady: %v", err)
+ }
+
+ wctx, cancel := context.WithTimeout(ctx, 3*time.Second)
+ defer cancel()
+ watchCh := c.Watch(wctx, "/foo", clientv3.WithPrefix())
+
+ var latestRev int64
+ for i := 0; i < 5; i++ {
+ resp, err := client.Put(ctx, fmt.Sprintf("/bar/out-%d", i), "v")
+ if err != nil {
+ t.Fatalf("Put(/bar/out-%d): %v", i, err)
+ }
+ latestRev = resp.Header.Revision
+ }
+
+ if err := client.RequestProgress(ctx); err != nil {
+ t.Fatalf("RequestProgress: %v", err)
+ }
+
+ var progressRev int64
+ select {
+ case resp, ok := <-watchCh:
+ if !ok || resp.Canceled {
+ t.Fatalf("expected active watch (not canceled), got %+v (closed=%v)", resp, !ok)
+ }
+ if len(resp.Events) != 0 {
+ t.Fatalf("expected a progress notification (no events), got %d event(s)", len(resp.Events))
+ }
+ if !resp.IsProgressNotify() {
+ t.Fatalf("expected IsProgressNotify()==true, got false (resp: %+v)", resp)
+ }
+ progressRev = resp.Header.Revision
+ if progressRev < latestRev {
+ t.Fatalf("progress revision %d < latest outside-prefix rev %d", progressRev, latestRev)
+ }
+ case <-wctx.Done():
+ t.Fatalf("timed out waiting for progress notification: %v", wctx.Err())
+ }
+}
+
+func TestCacheRequestProgress(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ ctx := t.Context()
+
+ c, err := cache.New(client, "/foo")
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatalf("cache.WaitReady: %v", err)
+ }
+
+ wctx, cancel := context.WithTimeout(ctx, 5*time.Second)
+ defer cancel()
+ watchCh := c.Watch(wctx, "/foo", clientv3.WithPrefix())
+
+ // Write some keys under the watched prefix so the cache advances.
+ var latestRev int64
+ for i := 0; i < 3; i++ {
+ resp, err := client.Put(ctx, fmt.Sprintf("/foo/key-%d", i), "v")
+ if err != nil {
+ t.Fatalf("Put(/foo/key-%d): %v", i, err)
+ }
+ latestRev = resp.Header.Revision
+ }
+
+ // Drain the event responses so the watcher is caught up.
+ readEvents(wctx, t, watchCh, 3)
+
+ // Call RequestProgress on the cache — this should deliver a progress
+ // notification to the watcher with the cache's current revision.
+ if err := c.RequestProgress(ctx); err != nil {
+ t.Fatalf("RequestProgress: %v", err)
+ }
+
+ resp := waitForProgressNotify(wctx, t, watchCh)
+ if resp.Header.Revision < latestRev {
+ t.Fatalf("progress revision %d < latest rev %d", resp.Header.Revision, latestRev)
+ }
+}
+
+func TestCacheWithProgressNotify(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ ctx := t.Context()
+
+ progressInterval := 200 * time.Millisecond
+ c, err := cache.New(client, "/foo", cache.WithProgressNotifyInterval(progressInterval))
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatalf("cache.WaitReady: %v", err)
+ }
+
+ t.Run("progress notification fires on idle watcher", func(t *testing.T) {
+ wctx, cancel := context.WithTimeout(ctx, 5*progressInterval)
+ defer cancel()
+
+ watchCh := c.Watch(wctx, "/foo", clientv3.WithPrefix(), clientv3.WithProgressNotify())
+
+ // Write a key so the cache has a non-zero revision.
+ if _, err := client.Put(ctx, "/foo/a", "1"); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ readEvents(wctx, t, watchCh, 1)
+
+ // Wait for a progress notification (should arrive after ~progressInterval of idle time).
+ resp := waitForProgressNotify(wctx, t, watchCh)
+ if resp.Header.Revision == 0 {
+ t.Fatalf("progress notification has zero revision")
+ }
+ })
+
+ t.Run("no progress notification without WithProgressNotify", func(t *testing.T) {
+ wctx, cancel := context.WithTimeout(ctx, 5*progressInterval)
+ defer cancel()
+
+ watchCh := c.Watch(wctx, "/foo", clientv3.WithPrefix())
+
+ // Write a key so the watcher gets an event.
+ if _, err := client.Put(ctx, "/foo/b", "2"); err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ readEvents(wctx, t, watchCh, 1)
+
+ // Wait longer than the progress interval — should NOT get a progress notification.
+ select {
+ case resp := <-watchCh:
+ if resp.IsProgressNotify() {
+ t.Fatalf("got unexpected progress notification on watcher without WithProgressNotify")
+ }
+ case <-time.After(3 * progressInterval):
+ }
+ })
+
+ t.Run("progress notification is elided when events are flowing", func(t *testing.T) {
+ wctx, cancel := context.WithTimeout(ctx, 5*time.Second)
+ defer cancel()
+
+ watchCh := c.Watch(wctx, "/foo", clientv3.WithPrefix(), clientv3.WithProgressNotify())
+
+ // Continuously write keys faster than the progress interval.
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ for i := 0; i < 10; i++ {
+ if _, err := client.Put(ctx, fmt.Sprintf("/foo/stream-%d", i), fmt.Sprintf("%d", i)); err != nil {
+ return
+ }
+ time.Sleep(progressInterval / 5)
+ }
+ }()
+
+ // Collect responses while writes are happening.
+ var gotProgress bool
+ eventCount := 0
+ collectLoop:
+ for {
+ select {
+ case resp, ok := <-watchCh:
+ if !ok {
+ break collectLoop
+ }
+ if resp.IsProgressNotify() {
+ gotProgress = true
+ }
+ eventCount += len(resp.Events)
+ if eventCount >= 10 {
+ break collectLoop
+ }
+ case <-wctx.Done():
+ break collectLoop
+ }
+ }
+ <-done
+
+ if gotProgress {
+ t.Logf("progress notification received during active writes (elision not guaranteed but noted)")
+ }
+ if eventCount != 10 {
+ t.Fatalf("expected exactly 10 events, got %d", eventCount)
+ }
+ })
+}
+
+func TestCacheWithoutPrefixGet(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("grpc proxy currently does not support requesting progress notifications")
+ }
+ tcs := []struct {
+ name string
+ initialEvents, followupEvents []*clientv3.Event
+ }{
+ {"watch-early (no pre-events)", nil, TestGetEvents},
+ {"watch-mid (partial pre-events)", filterEvents(TestGetEvents, revLessThan(4)), filterEvents(TestGetEvents, revGreaterEqual(4))},
+ {"watch-late (all pre-events)", TestGetEvents, nil},
+ }
+
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client, kv := clus.Client(0), clus.Client(0).KV
+
+ testGet(t, kv, func() Getter {
+ c, err := cache.New(client, "")
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(t.Context()); err != nil {
+ t.Fatalf("cache not ready: %v", err)
+ }
+ return c
+ }, tc.initialEvents, tc.followupEvents)
+ })
+ }
+}
+
+func TestGet(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+
+ client := clus.Client(0)
+ kv := client.KV
+
+ testGet(t, kv, func() Getter { return kv }, TestGetEvents, nil)
+}
+
+func testGet(t *testing.T, kv clientv3.KV, getReader func() Getter, initialEvents, followupEvents []*clientv3.Event) {
+ ctx := t.Context()
+ t.Log("Setup")
+ baseRev := applyEvents(ctx, t, kv, initialEvents)
+
+ reader := getReader()
+ if c, ok := reader.(*cache.Cache); ok {
+ if err := c.WaitForRevision(ctx, baseRev); err != nil {
+ t.Fatalf("cache never caught up to rev %d: %v", baseRev, err)
+ }
+ }
+
+ followupRev := applyEvents(ctx, t, kv, followupEvents)
+ if c, ok := reader.(*cache.Cache); ok {
+ if err := c.WaitForRevision(ctx, followupRev); err != nil {
+ t.Fatalf("cache never caught up to rev %d: %v", followupRev, err)
+ }
+ }
+
+ if followupRev > baseRev {
+ baseRev = followupRev
+ }
+
+ latestRev := baseRev
+
+ t.Log("Validate")
+ for _, tc := range getTestCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ latestRev += advanceRevision(t, kv)
+
+ opts := tc.opts
+ if tc.optsFunc != nil {
+ opts = append(opts, tc.optsFunc(latestRev)...)
+ }
+
+ op := clientv3.OpGet(tc.key, opts...)
+ requestedRev := op.Rev()
+
+ resp, err := reader.Get(ctx, tc.key, opts...)
+ if tc.expectErr != nil {
+ if !errors.Is(err, tc.expectErr) {
+ t.Fatalf("expected %v for Get %q; got %v", tc.expectErr, tc.key, err)
+ }
+ return
+ }
+ if err != nil {
+ if _, ok := reader.(*cache.Cache); ok && requestedRev > 0 && requestedRev < baseRev && errors.Is(err, rpctypes.ErrCompacted) {
+ t.Logf("expected ErrCompacted: requestedRev=%d < baseCompleteRev=%d", requestedRev, baseRev)
+ return
+ }
+ t.Fatalf("Get %q failed: %v", tc.key, err)
+ }
+ if diff := cmp.Diff(tc.wantKVs, resp.Kvs, protocmp.Transform()); diff != "" {
+ t.Fatalf("unexpected KVs (-want +got):\n%s", diff)
+ }
+ if op.IsSerializable() {
+ if resp.Header.Revision < baseRev {
+ t.Fatalf("revision: got %d, want >= %d", resp.Header.Revision, baseRev)
+ }
+ } else {
+ if resp.Header.Revision != latestRev {
+ t.Fatalf("revision: got %d, want == %d", resp.Header.Revision, latestRev)
+ }
+ }
+ })
+ }
+}
+
+var TestGetEvents = []*clientv3.Event{
+ Rev2PutFooA, Rev3PutFooB, Rev4PutFooC, Rev5PutFooD, Rev6DeleteFooD, Rev7TxnPutFooA, Rev7TxnPutFooB, Rev8PutFooA,
+}
+
+var (
+ Rev2PutFooA = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("a1"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ Rev3PutFooB = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/b"),
+ Value: []byte("b1"),
+ CreateRevision: 3,
+ ModRevision: 3,
+ Version: 1,
+ },
+ }
+ Rev4PutFooC = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/c"),
+ Value: []byte("c1"),
+ CreateRevision: 4,
+ ModRevision: 4,
+ Version: 1,
+ },
+ }
+ Rev5PutFooD = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/d"),
+ Value: []byte("d1"),
+ CreateRevision: 5,
+ ModRevision: 5,
+ Version: 1,
+ },
+ }
+ Rev6DeleteFooD = &clientv3.Event{
+ Type: clientv3.EventTypeDelete,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/d"),
+ ModRevision: 6,
+ },
+ }
+ Rev7TxnPutFooA = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("a2"),
+ CreateRevision: 2,
+ ModRevision: 7,
+ Version: 2,
+ },
+ }
+ Rev7TxnPutFooB = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/b"),
+ Value: []byte("b2"),
+ CreateRevision: 3,
+ ModRevision: 7,
+ Version: 2,
+ },
+ }
+ Rev8PutFooA = &clientv3.Event{
+ Type: clientv3.EventTypePut,
+ Kv: &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("a3"),
+ CreateRevision: 2,
+ ModRevision: 8,
+ Version: 3,
+ },
+ }
+)
+
+type getTestCase struct {
+ name string
+ key string
+ opts []clientv3.OpOption
+ optsFunc func(latestRev int64) []clientv3.OpOption
+ wantKVs []*mvccpb.KeyValue
+ expectErr error
+}
+
+var getTestCases = []getTestCase{
+ {
+ name: "single key /foo/a",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv},
+ },
+ {
+ name: "single key /foo/a at rev=2",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(2)},
+ wantKVs: []*mvccpb.KeyValue{Rev2PutFooA.Kv},
+ },
+ {
+ name: "single key /foo/a at rev=7",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(7)},
+ wantKVs: []*mvccpb.KeyValue{Rev7TxnPutFooA.Kv},
+ },
+ {
+ name: "single key /foo/a at rev=latest",
+ key: "/foo/a",
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev)}
+ },
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv},
+ },
+ {
+ name: "single key /foo/a at rev=latest+1 (future), returns error",
+ key: "/foo/a",
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "non-existing key",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{},
+ wantKVs: nil,
+ },
+ {
+ name: "non-existing key at rev=4",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithRev(4)},
+ wantKVs: nil,
+ },
+ {
+ name: "non-existing key at rev=latest+1 (future), returns error",
+ key: "/doesnotexist",
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "prefix /foo",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv, Rev7TxnPutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "prefix /foo at rev=5",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(5)},
+ wantKVs: []*mvccpb.KeyValue{Rev2PutFooA.Kv, Rev3PutFooB.Kv, Rev4PutFooC.Kv, Rev5PutFooD.Kv},
+ },
+ {
+ name: "prefix /foo/b at rev=4",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(4)},
+ wantKVs: []*mvccpb.KeyValue{Rev3PutFooB.Kv},
+ },
+ {
+ name: "prefix /foo/b at rev=7",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(7)},
+ wantKVs: []*mvccpb.KeyValue{Rev7TxnPutFooB.Kv},
+ },
+ {
+ name: "prefix /foo at rev=latest+1 (future), returns error",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "range [/foo/a, /foo/c)",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/c")},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv, Rev7TxnPutFooB.Kv},
+ },
+ {
+ name: "range [/foo/a, /foo/d) at rev=5",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/d"), clientv3.WithRev(5)},
+ wantKVs: []*mvccpb.KeyValue{Rev2PutFooA.Kv, Rev3PutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "range [/foo/a, /foo/c) at rev=latest+1 (future), returns error",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/c")},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "fromKey /foo/b",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ wantKVs: []*mvccpb.KeyValue{Rev7TxnPutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "fromKey /foo/b at rev=7",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithRev(7)},
+ wantKVs: []*mvccpb.KeyValue{Rev7TxnPutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "fromKey /foo/b at rev=latest+1 (future), returns error",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "single key /foo/a serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv},
+ },
+ {
+ name: "single key /foo/a serializable at rev=latest+1 (future), returns error",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "non-existing key serializable",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ wantKVs: nil,
+ },
+ {
+ name: "non-existing key serializable at rev=latest+1 (future), returns error",
+ key: "/doesnotexist",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "prefix /foo serializable",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv, Rev7TxnPutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "prefix /foo serializable at rev=latest+1 (future), returns error",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithSerializable()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "range [/foo/a, /foo/c) serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/c"), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{Rev8PutFooA.Kv, Rev7TxnPutFooB.Kv},
+ },
+ {
+ name: "range [/foo/a, /foo/c) serializable at rev=latest+1 (future), returns error",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/c"), clientv3.WithSerializable()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+ {
+ name: "fromKey /foo/b serializable",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{Rev7TxnPutFooB.Kv, Rev4PutFooC.Kv},
+ },
+ {
+ name: "fromKey /foo/b serializable at rev=latest+1 (future), returns error",
+ key: "/foo/b",
+ opts: []clientv3.OpOption{clientv3.WithFromKey(), clientv3.WithSerializable()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev + 1)}
+ },
+ expectErr: rpctypes.ErrFutureRev,
+ },
+}
+
+func TestCacheWithPrefixGetInScope(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("grpc proxy currently does not support requesting progress notifications")
+ }
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ cli := clus.Client(0)
+
+ testWithPrefixGet(t, cli, func() Getter {
+ c, err := cache.New(cli, "/foo")
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ t.Cleanup(c.Close)
+ if err := c.WaitReady(t.Context()); err != nil {
+ t.Fatalf("cache.WaitReady: %v", err)
+ }
+ return c
+ })
+}
+
+func TestWithPrefixGet(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ cli := clus.Client(0)
+
+ testWithPrefixGet(t, cli, func() Getter { return cli.KV })
+}
+
+func testWithPrefixGet(t *testing.T, cli *clientv3.Client, getReader func() Getter) {
+ ctx := t.Context()
+ seedResp, err := cli.Put(ctx, "/foo/a", "val1")
+ if err != nil {
+ t.Fatalf("seed put: %v", err)
+ }
+ seedRev := seedResp.Header.Revision
+
+ reader := getReader()
+
+ for i := 0; i < 5; i++ {
+ _, err = cli.Put(ctx, fmt.Sprintf("/bar/x%d", i), fmt.Sprintf("%d", i))
+ if err != nil {
+ t.Fatalf("advance put: %v", err)
+ }
+ }
+
+ resp, err := cli.Put(ctx, "/foo/a", "val2")
+ if err != nil {
+ t.Fatalf("second put: %v", err)
+ }
+ latestRev := resp.Header.Revision
+
+ if err := cli.RequestProgress(ctx); err != nil {
+ t.Fatalf("RequestProgress: %v", err)
+ }
+
+ if c, ok := reader.(*cache.Cache); ok {
+ if err := c.WaitForRevision(ctx, latestRev); err != nil {
+ t.Fatalf("cache didn’t observe progress to rev %d: %v", latestRev, err)
+ }
+ }
+
+ expectedFooA := &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("val2"),
+ CreateRevision: seedRev,
+ ModRevision: latestRev,
+ Version: 2,
+ }
+
+ expectedOldFooA := &mvccpb.KeyValue{
+ Key: []byte("/foo/a"),
+ Value: []byte("val1"),
+ CreateRevision: seedRev,
+ ModRevision: seedRev,
+ Version: 1,
+ }
+
+ baseRev := latestRev
+
+ testCases := []struct {
+ name string
+ key string
+ opts []clientv3.OpOption
+ optsFunc func(latestRev int64) []clientv3.OpOption
+ wantKVs []*mvccpb.KeyValue
+ }{
+ {
+ name: "single key within cache prefix",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "single key within cache prefix at latest/progress rev",
+ key: "/foo/a",
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev)}
+ },
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "prefix query within cache prefix",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "prefix query within cache prefix at latest/progress rev",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix()},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev)}
+ },
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "range query within cache prefix",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/b")},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "range query within cache prefix at latest/progress rev",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/z")},
+ optsFunc: func(latestRev int64) []clientv3.OpOption {
+ return []clientv3.OpOption{clientv3.WithRev(latestRev)}
+ },
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "single key within cache prefix serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "single key within cache prefix at base rev serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable(), clientv3.WithRev(baseRev)},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "prefix query within cache prefix serializable",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "prefix query within cache prefix at base rev serializable",
+ key: "/foo",
+ opts: []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithSerializable(), clientv3.WithRev(baseRev)},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "range query within cache prefix serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/b"), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "range query within cache prefix at base rev serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRange("/foo/z"), clientv3.WithSerializable(), clientv3.WithRev(baseRev)},
+ wantKVs: []*mvccpb.KeyValue{expectedFooA},
+ },
+ {
+ name: "prefix query within cache prefix at intermediate rev serializable",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(seedRev + 1), clientv3.WithSerializable()},
+ wantKVs: []*mvccpb.KeyValue{expectedOldFooA},
+ },
+ {
+ name: "prefix query within cache prefix at intermediate rev",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithRev(seedRev + 1)},
+ wantKVs: []*mvccpb.KeyValue{expectedOldFooA},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ latestRev += advanceRevision(t, cli)
+
+ opts := tc.opts
+ if tc.optsFunc != nil {
+ opts = append(opts, tc.optsFunc(latestRev)...)
+ }
+ op := clientv3.OpGet(tc.key, opts...)
+ resp, err := reader.Get(ctx, tc.key, opts...)
+ if err != nil {
+ t.Fatalf("Get(%q): %v", tc.key, err)
+ }
+
+ if diff := cmp.Diff(tc.wantKVs, resp.Kvs, protocmp.Transform()); diff != "" {
+ t.Errorf("unexpected KVs (-want +got):\n%s", diff)
+ }
+
+ if op.IsSerializable() {
+ if resp.Header.Revision < baseRev {
+ t.Errorf("Header.Revision=%d; want >= %d", resp.Header.Revision, baseRev)
+ }
+ } else {
+ if resp.Header.Revision != latestRev {
+ t.Errorf("Header.Revision=%d; want: %d", resp.Header.Revision, latestRev)
+ }
+ }
+ })
+ }
+}
+
+func TestCacheWithPrefixGetOutOfScope(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("grpc proxy currently does not support requesting progress notifications")
+ }
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ cli := clus.Client(0)
+ c, err := cache.New(cli, "/foo")
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ defer c.Close()
+ ctx := t.Context()
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatalf("cache.WaitReady: %v", err)
+ }
+
+ cases := []struct {
+ name string
+ key string
+ opts []clientv3.OpOption
+ }{
+ {
+ name: "single key outside prefix",
+ key: "/bar/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable()},
+ },
+ {
+ name: "prefix() outside cache prefix",
+ key: "/bar",
+ opts: []clientv3.OpOption{clientv3.WithSerializable(), clientv3.WithPrefix()},
+ },
+ {
+ name: "range crossing cache boundary",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable(), clientv3.WithRange("/zzz")},
+ },
+ {
+ name: "fromKey disallowed with cache prefix",
+ key: "/foo/a",
+ opts: []clientv3.OpOption{clientv3.WithSerializable(), clientv3.WithFromKey()},
+ },
+ }
+
+ for _, tc := range cases {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ _, err := c.Get(ctx, tc.key, tc.opts...)
+ if !errors.Is(err, cache.ErrKeyRangeInvalid) {
+ t.Fatalf("expected ErrKeyRangeInvalid; got %v", err)
+ }
+ })
+ }
+}
+
+func TestCacheLaggingWatcher(t *testing.T) {
+ const prefix = "/test/"
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ tests := []struct {
+ name string
+ window int
+ eventCount int
+ wantExactEventCount int
+ wantAtMaxEventCount int
+ wantClosed bool
+ }{
+ {
+ name: "all event fit",
+ window: 10,
+ eventCount: 9,
+ wantExactEventCount: 9,
+ wantClosed: false,
+ },
+ {
+ name: "events fill window",
+ window: 10,
+ eventCount: 10,
+ wantExactEventCount: 10,
+ wantClosed: false,
+ },
+ {
+ name: "event fill pipeline",
+ window: 10,
+ eventCount: 11,
+ wantExactEventCount: 11,
+ wantClosed: false,
+ },
+ {
+ name: "pipeline overflow",
+ window: 10,
+ eventCount: 12,
+ wantAtMaxEventCount: 1, // Either 0 or 1.
+ wantClosed: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := t.Context()
+ c, err := cache.New(
+ client, prefix,
+ cache.WithHistoryWindowSize(tt.window),
+ cache.WithPerWatcherBufferSize(0),
+ cache.WithResyncInterval(10*time.Millisecond),
+ )
+ if err != nil {
+ t.Fatalf("New(...): %v", err)
+ }
+ defer c.Close()
+
+ if err := c.WaitReady(ctx); err != nil {
+ t.Fatalf("cache not ready: %v", err)
+ }
+ ch := c.Watch(ctx, prefix, clientv3.WithPrefix())
+ if err := c.WaitForNextResync(ctx); err != nil {
+ t.Fatalf("cache not synced: %v", err)
+ }
+
+ generateEvents(t, client, prefix, tt.eventCount)
+ if err := c.WaitForNextResync(ctx); err != nil {
+ t.Fatalf("cache not synced: %v", err)
+ }
+ gotEvents, ok := collectAndAssertAtomicEvents(t, ch)
+ closed := !ok
+
+ if tt.wantExactEventCount != 0 && tt.wantExactEventCount != len(gotEvents) {
+ t.Errorf("gotEvents=%v, wantEvents=%v", len(gotEvents), tt.wantExactEventCount)
+ }
+ if tt.wantAtMaxEventCount != 0 && len(gotEvents) > tt.wantAtMaxEventCount {
+ t.Errorf("gotEvents=%v, wantEvents<%v", len(gotEvents), tt.wantAtMaxEventCount)
+ }
+ if closed != tt.wantClosed {
+ t.Errorf("closed=%v, wantClosed=%v", closed, tt.wantClosed)
+ }
+ })
+ }
+}
+
+func TestCacheUnsupportedWatchOptions(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ c, err := cache.New(client, "", cache.WithHistoryWindowSize(1))
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ defer c.Close()
+ if err := c.WaitReady(t.Context()); err != nil {
+ t.Fatalf("cache not ready: %v", err)
+ }
+
+ unsupported := []struct {
+ name string
+ opt clientv3.OpOption
+ }{
+ {"WithPrevKV", clientv3.WithPrevKV()},
+ {"WithFragment", clientv3.WithFragment()},
+ {"WithCreatedNotify", clientv3.WithCreatedNotify()},
+ {"WithFilterPut", clientv3.WithFilterPut()},
+ {"WithFilterDelete", clientv3.WithFilterDelete()},
+ }
+
+ for _, tc := range unsupported {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ ch := c.Watch(t.Context(), "foo", tc.opt)
+
+ resp, ok := <-ch
+ if !ok {
+ t.Fatalf("channel closed without yielding a response")
+ }
+ if !resp.Canceled {
+ t.Errorf("expected Canceled=true, got %+v", resp)
+ }
+ if !strings.Contains(resp.Err().Error(), cache.ErrUnsupportedRequest.Error()) {
+ t.Errorf("expected ErrUnsupportedWatch text %q, got %v",
+ cache.ErrUnsupportedRequest.Error(), resp.Err())
+ }
+ })
+ }
+}
+
+func TestCacheUnsupportedGetOptions(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("grpc proxy currently does not support requesting progress notifications")
+ }
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ t.Cleanup(func() { clus.Terminate(t) })
+ client := clus.Client(0)
+
+ c, err := cache.New(client, "", cache.WithHistoryWindowSize(1))
+ if err != nil {
+ t.Fatalf("cache.New: %v", err)
+ }
+ defer c.Close()
+ if err := c.WaitReady(t.Context()); err != nil {
+ t.Fatalf("cache not ready: %v", err)
+ }
+
+ unsupported := []struct {
+ name string
+ opts []clientv3.OpOption
+ }{
+ {"WithCountOnly", []clientv3.OpOption{clientv3.WithCountOnly()}},
+ {"WithLimit", []clientv3.OpOption{clientv3.WithLimit(1)}},
+ {"WithSort", []clientv3.OpOption{clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend)}},
+ {"WithPrevKV", []clientv3.OpOption{clientv3.WithPrevKV()}},
+ {"WithMinModRevision", []clientv3.OpOption{clientv3.WithMinModRev(2)}},
+ {"WithMaxModRevision", []clientv3.OpOption{clientv3.WithMaxModRev(10)}},
+ {"WithMinCreateRevision", []clientv3.OpOption{clientv3.WithMinCreateRev(3)}},
+ {"WithMaxCreateRevision", []clientv3.OpOption{clientv3.WithMaxCreateRev(5)}},
+ }
+
+ for _, tc := range unsupported {
+ tc := tc
+ t.Run(tc.name, func(t *testing.T) {
+ _, err := c.Get(t.Context(), "foo", tc.opts...)
+ if !errors.Is(err, cache.ErrUnsupportedRequest) {
+ t.Errorf("Get with %s: expected ErrUnsupportedRequest, got %v", tc.name, err)
+ }
+ })
+ }
+}
+
+func generateEvents(t *testing.T, client *clientv3.Client, prefix string, n int) {
+ t.Helper()
+ for i := 0; i < n; i++ {
+ key := fmt.Sprintf("%s%d", prefix, i)
+ if _, err := client.Put(t.Context(), key, fmt.Sprintf("%d", i)); err != nil {
+ t.Fatalf("Put(%q): %v", key, err)
+ }
+ }
+}
+
+type Watcher interface {
+ Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan
+}
+
+type Getter interface {
+ Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error)
+}
+
+func collectAndAssertAtomicEvents(t *testing.T, watch clientv3.WatchChan) (events []*clientv3.Event, ok bool) {
+ deadline := time.After(time.Second)
+ var lastRevision int64
+
+ for {
+ select {
+ case resp, ok := <-watch:
+ if !ok {
+ return events, false
+ }
+ if len(resp.Events) != 0 && resp.Events[0].Kv.ModRevision == lastRevision {
+ t.Fatalf("same revision found as in previous response: %d", lastRevision)
+ }
+ for _, ev := range resp.Events {
+ if ev.Kv.ModRevision < lastRevision {
+ t.Fatalf("revision went backwards: last %d, now %d", lastRevision, ev.Kv.ModRevision)
+ }
+ events = append(events, ev)
+ lastRevision = ev.Kv.ModRevision
+ }
+ case <-deadline:
+ return events, true
+ case <-time.After(100 * time.Millisecond):
+ return events, true
+ }
+ }
+}
+
+func readEvents(ctx context.Context, t *testing.T, watchCh clientv3.WatchChan, n int) {
+ t.Helper()
+ received := 0
+ for received < n {
+ select {
+ case resp := <-watchCh:
+ if resp.Canceled {
+ t.Fatalf("unexpected canceled response: %v", resp.CancelReason)
+ }
+ received += len(resp.Events)
+ case <-ctx.Done():
+ t.Fatalf("timed out draining events, got %d/%d", received, n)
+ }
+ }
+}
+
+func waitForProgressNotify(ctx context.Context, t *testing.T, watchCh clientv3.WatchChan) clientv3.WatchResponse {
+ t.Helper()
+ for {
+ select {
+ case resp, ok := <-watchCh:
+ if !ok {
+ t.Fatalf("watch channel closed while waiting for progress notification")
+ }
+ if resp.Canceled {
+ t.Fatalf("unexpected canceled: %v", resp.CancelReason)
+ }
+ if resp.IsProgressNotify() {
+ return resp
+ }
+ case <-ctx.Done():
+ t.Fatalf("timed out waiting for progress notification")
+ return clientv3.WatchResponse{}
+ }
+ }
+}
+
+func advanceRevision(t *testing.T, kv clientv3.KV) int64 {
+ t.Helper()
+ n := rand.Intn(3) + 3
+ for i := 0; i < n; i++ {
+ _, err := kv.Put(t.Context(), fmt.Sprintf("/bar/%d/%s", i, stringutil.RandString(10)), "v")
+ if err != nil {
+ t.Fatalf("Put: %v", err)
+ }
+ }
+ return int64(n)
+}
+
+func applyEvents(ctx context.Context, t *testing.T, kv clientv3.KV, evs []*clientv3.Event) int64 {
+ var lastRev int64
+ for _, batches := range batchEventsByRevision(evs) {
+ lastRev = applyEventBatch(ctx, t, kv, batches)
+ }
+ return lastRev
+}
+
+func batchEventsByRevision(events []*clientv3.Event) [][]*clientv3.Event {
+ var batches [][]*clientv3.Event
+ if len(events) == 0 {
+ return batches
+ }
+ start := 0
+ for end := 1; end < len(events); end++ {
+ if events[end].Kv.ModRevision != events[start].Kv.ModRevision {
+ batches = append(batches, events[start:end])
+ start = end
+ }
+ }
+ batches = append(batches, events[start:])
+ return batches
+}
+
+func applyEventBatch(ctx context.Context, t *testing.T, kv clientv3.KV, batch []*clientv3.Event) int64 {
+ ops := make([]clientv3.Op, 0, len(batch))
+ for _, event := range batch {
+ switch event.Type {
+ case clientv3.EventTypePut:
+ ops = append(ops, clientv3.OpPut(string(event.Kv.Key), string(event.Kv.Value)))
+ case clientv3.EventTypeDelete:
+ ops = append(ops, clientv3.OpDelete(string(event.Kv.Key)))
+ default:
+ t.Fatalf("unsupported event type: %v", event.Type)
+ }
+ }
+ resp, err := kv.Txn(ctx).Then(ops...).Commit()
+ if err != nil {
+ t.Fatalf("Txn failed: %v", err)
+ }
+ return resp.Header.Revision
+}
+
+func filterEvents(evs []*clientv3.Event, pred func(int64) bool) []*clientv3.Event {
+ var out []*clientv3.Event
+ for _, ev := range evs {
+ if pred(ev.Kv.ModRevision) {
+ out = append(out, ev)
+ }
+ }
+ return out
+}
+
+func revLessThan(n int64) func(int64) bool { return func(r int64) bool { return r < n } }
+func revGreaterEqual(n int64) func(int64) bool { return func(r int64) bool { return r >= n } }
diff --git a/tests/integration/clientv3/cluster_test.go b/tests/integration/clientv3/cluster_test.go
index 055da70f3e7f..43b365ba2d07 100644
--- a/tests/integration/clientv3/cluster_test.go
+++ b/tests/integration/clientv3/cluster_test.go
@@ -25,19 +25,21 @@ import (
"github.com/stretchr/testify/require"
+ "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMemberList(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
capi := clus.RandClient()
- resp, err := capi.MemberList(context.Background())
+ resp, err := capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -48,15 +50,15 @@ func TestMemberList(t *testing.T) {
}
func TestMemberAdd(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
capi := clus.RandClient()
urls := []string{"http://127.0.0.1:1234"}
- resp, err := capi.MemberAdd(context.Background(), urls)
+ resp, err := capi.MemberAdd(t.Context(), urls)
if err != nil {
t.Fatalf("failed to add member %v", err)
}
@@ -67,20 +69,20 @@ func TestMemberAdd(t *testing.T) {
}
func TestMemberAddWithExistingURLs(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
capi := clus.RandClient()
- resp, err := capi.MemberList(context.Background())
+ resp, err := capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
existingURL := resp.Members[0].PeerURLs[0]
- _, err = capi.MemberAdd(context.Background(), []string{existingURL})
+ _, err = capi.MemberAdd(t.Context(), []string{existingURL})
expectedErrKeywords := "Peer URLs already exists"
if err == nil {
t.Fatalf("expecting add member to fail, got no error")
@@ -91,13 +93,13 @@ func TestMemberAddWithExistingURLs(t *testing.T) {
}
func TestMemberRemove(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
capi := clus.Client(1)
- resp, err := capi.MemberList(context.Background())
+ resp, err := capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -113,12 +115,12 @@ func TestMemberRemove(t *testing.T) {
}
}
- _, err = capi.MemberRemove(context.Background(), rmvID)
+ _, err = capi.MemberRemove(t.Context(), rmvID)
if err != nil {
t.Fatalf("failed to remove member %v", err)
}
- resp, err = capi.MemberList(context.Background())
+ resp, err = capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -129,24 +131,24 @@ func TestMemberRemove(t *testing.T) {
}
func TestMemberUpdate(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
capi := clus.RandClient()
- resp, err := capi.MemberList(context.Background())
+ resp, err := capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
urls := []string{"http://127.0.0.1:1234"}
- _, err = capi.MemberUpdate(context.Background(), resp.Members[0].ID, urls)
+ _, err = capi.MemberUpdate(t.Context(), resp.Members[0].ID, urls)
if err != nil {
t.Fatalf("failed to update member %v", err)
}
- resp, err = capi.MemberList(context.Background())
+ resp, err = capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -156,10 +158,37 @@ func TestMemberUpdate(t *testing.T) {
}
}
+func TestMemberUpdateLearner(t *testing.T) {
+ integration.BeforeTest(t)
+
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ defer clus.Terminate(t)
+
+ capi := clus.RandClient()
+
+ urls := []string{"http://127.0.0.1:1234"}
+ addResp, err := capi.MemberAddAsLearner(t.Context(), urls)
+ require.NoError(t, err)
+ learnerID := addResp.Member.ID
+
+ learner, err := getMemberByID(t.Context(), capi, learnerID)
+ require.NoError(t, err)
+ require.Truef(t, learner.IsLearner, "added a member as learner, IsLearner is %t", learner.IsLearner)
+
+ updatedURLs := []string{"http://127.0.0.1:5678"}
+ _, err = capi.MemberUpdate(t.Context(), learnerID, updatedURLs)
+ require.NoError(t, err)
+
+ learner, err = getMemberByID(t.Context(), capi, learnerID)
+ require.NoError(t, err)
+ require.Equal(t, learner.PeerURLs, updatedURLs)
+ require.Truef(t, learner.IsLearner, "updated peer address of a learner member, IsLearner is %t", learner.IsLearner)
+}
+
func TestMemberAddUpdateWrongURLs(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
capi := clus.RandClient()
@@ -178,11 +207,11 @@ func TestMemberAddUpdateWrongURLs(t *testing.T) {
{"localhost:1234"},
}
for i := range tt {
- _, err := capi.MemberAdd(context.Background(), tt[i])
+ _, err := capi.MemberAdd(t.Context(), tt[i])
if err == nil {
t.Errorf("#%d: MemberAdd err = nil, but error", i)
}
- _, err = capi.MemberUpdate(context.Background(), 0, tt[i])
+ _, err = capi.MemberUpdate(t.Context(), 0, tt[i])
if err == nil {
t.Errorf("#%d: MemberUpdate err = nil, but error", i)
}
@@ -190,15 +219,15 @@ func TestMemberAddUpdateWrongURLs(t *testing.T) {
}
func TestMemberAddForLearner(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
capi := clus.RandClient()
urls := []string{"http://127.0.0.1:1234"}
- resp, err := capi.MemberAddAsLearner(context.Background(), urls)
+ resp, err := capi.MemberAddAsLearner(t.Context(), urls)
if err != nil {
t.Fatalf("failed to add member %v", err)
}
@@ -219,9 +248,9 @@ func TestMemberAddForLearner(t *testing.T) {
}
func TestMemberPromote(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
// member promote request can be sent to any server in cluster,
@@ -234,7 +263,7 @@ func TestMemberPromote(t *testing.T) {
learnerMember := clus.MustNewMember(t)
urls := learnerMember.PeerURLs.StringSlice()
- memberAddResp, err := capi.MemberAddAsLearner(context.Background(), urls)
+ memberAddResp, err := capi.MemberAddAsLearner(t.Context(), urls)
if err != nil {
t.Fatalf("failed to add member %v", err)
}
@@ -256,7 +285,7 @@ func TestMemberPromote(t *testing.T) {
// learner is not started yet. Expect learner progress check to fail.
// As the result, member promote request will fail.
- _, err = capi.MemberPromote(context.Background(), learnerID)
+ _, err = capi.MemberPromote(t.Context(), learnerID)
expectedErrKeywords := "can only promote a learner member which is in sync with leader"
if err == nil {
t.Fatalf("expecting promote not ready learner to fail, got no error")
@@ -280,7 +309,7 @@ func TestMemberPromote(t *testing.T) {
t.Fatalf("failed all attempts to promote learner member, last error: %v", err)
}
- _, err = capi.MemberPromote(context.Background(), learnerID)
+ _, err = capi.MemberPromote(t.Context(), learnerID)
// successfully promoted learner
if err == nil {
break
@@ -295,9 +324,9 @@ func TestMemberPromote(t *testing.T) {
// TestMemberPromoteMemberNotLearner ensures that promoting a voting member fails.
func TestMemberPromoteMemberNotLearner(t *testing.T) {
- integration2.BeforeTest(t, integration2.WithFailpoint("raftBeforeAdvance", `sleep(100)`))
+ integration.BeforeTest(t, integration.WithFailpoint("raftBeforeAdvance", `sleep(100)`))
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// member promote request can be sent to any server in cluster,
@@ -308,7 +337,7 @@ func TestMemberPromoteMemberNotLearner(t *testing.T) {
followerIdx := (leaderIdx + 1) % 3
cli := clus.Client(followerIdx)
- resp, err := cli.MemberList(context.Background())
+ resp, err := cli.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -319,7 +348,7 @@ func TestMemberPromoteMemberNotLearner(t *testing.T) {
// promoting any of the voting members in cluster should fail
expectedErrKeywords := "can only promote a learner member"
for _, m := range resp.Members {
- _, err = cli.MemberPromote(context.Background(), m.ID)
+ _, err = cli.MemberPromote(t.Context(), m.ID)
if err == nil {
t.Fatalf("expect promoting voting member to fail, got no error")
}
@@ -331,9 +360,9 @@ func TestMemberPromoteMemberNotLearner(t *testing.T) {
// TestMemberPromoteMemberNotExist ensures that promoting a member that does not exist in cluster fails.
func TestMemberPromoteMemberNotExist(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// member promote request can be sent to any server in cluster,
@@ -344,7 +373,7 @@ func TestMemberPromoteMemberNotExist(t *testing.T) {
followerIdx := (leaderIdx + 1) % 3
cli := clus.Client(followerIdx)
- resp, err := cli.MemberList(context.Background())
+ resp, err := cli.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to list member %v", err)
}
@@ -369,7 +398,7 @@ func TestMemberPromoteMemberNotExist(t *testing.T) {
}
expectedErrKeywords := "member not found"
- _, err = cli.MemberPromote(context.Background(), randID)
+ _, err = cli.MemberPromote(t.Context(), randID)
if err == nil {
t.Fatalf("expect promoting voting member to fail, got no error")
}
@@ -380,15 +409,15 @@ func TestMemberPromoteMemberNotExist(t *testing.T) {
// TestMaxLearnerInCluster verifies that the maximum number of learners allowed in a cluster
func TestMaxLearnerInCluster(t *testing.T) {
- integration2.BeforeTest(t, integration2.WithFailpoint("raftBeforeAdvance", `sleep(100)`))
+ integration.BeforeTest(t, integration.WithFailpoint("raftBeforeAdvance", `sleep(100)`))
// 1. start with a cluster with 3 voting member and max learner 2
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, MaxLearners: 2, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, MaxLearners: 2, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
// 2. adding 2 learner members should succeed
for i := 0; i < 2; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
_, err := clus.Client(0).MemberAddAsLearner(ctx, []string{fmt.Sprintf("http://127.0.0.1:123%d", i)})
cancel()
if err != nil {
@@ -399,7 +428,7 @@ func TestMaxLearnerInCluster(t *testing.T) {
// ensure client endpoint is voting member
leaderIdx := clus.WaitLeader(t)
capi := clus.Client(leaderIdx)
- resp1, err := capi.MemberList(context.Background())
+ resp1, err := capi.MemberList(t.Context())
if err != nil {
t.Fatalf("failed to get member list")
}
@@ -414,7 +443,7 @@ func TestMaxLearnerInCluster(t *testing.T) {
}
// 3. cluster has 3 voting member and 2 learner, adding another learner should fail
- _, err = clus.Client(0).MemberAddAsLearner(context.Background(), []string{"http://127.0.0.1:2342"})
+ _, err = clus.Client(0).MemberAddAsLearner(t.Context(), []string{"http://127.0.0.1:2342"})
if err == nil {
t.Fatalf("expect member add to fail, got no error")
}
@@ -424,8 +453,29 @@ func TestMaxLearnerInCluster(t *testing.T) {
}
// 4. cluster has 3 voting member and 1 learner, adding a voting member should succeed
- _, err = clus.Client(0).MemberAdd(context.Background(), []string{"http://127.0.0.1:3453"})
+ _, err = clus.Client(0).MemberAdd(t.Context(), []string{"http://127.0.0.1:3453"})
if err != nil {
t.Errorf("failed to add member %v", err)
}
}
+
+func getMemberByID(ctx context.Context, cli *clientv3.Client, id uint64) (member *etcdserverpb.Member, err error) {
+ var resp *clientv3.MemberListResponse
+ resp, err = cli.MemberList(ctx)
+ if err != nil {
+ return member, err
+ }
+
+ for _, m := range resp.Members {
+ if m.ID == id {
+ member = m
+ break
+ }
+ }
+
+ if member == nil {
+ err = fmt.Errorf("failed to find member by id %d", id)
+ }
+
+ return member, err
+}
diff --git a/tests/integration/clientv3/concurrency/election_test.go b/tests/integration/clientv3/concurrency/election_test.go
index 951c6a91fbf1..84cf9d476a22 100644
--- a/tests/integration/clientv3/concurrency/election_test.go
+++ b/tests/integration/clientv3/concurrency/election_test.go
@@ -21,15 +21,17 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestResumeElection(t *testing.T) {
const prefix = "/resume-election/"
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
if err != nil {
log.Fatal(err)
}
@@ -45,20 +47,16 @@ func TestResumeElection(t *testing.T) {
e := concurrency.NewElection(s, prefix)
// entire test should never take more than 10 seconds
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second*10)
defer cancel()
// become leader
- if err = e.Campaign(ctx, "candidate1"); err != nil {
- t.Fatalf("Campaign() returned non nil err: %s", err)
- }
+ require.NoErrorf(t, e.Campaign(ctx, "candidate1"), "Campaign() returned non nil err")
// get the leadership details of the current election
var leader *clientv3.GetResponse
leader, err = e.Leader(ctx)
- if err != nil {
- t.Fatalf("Leader() returned non nil err: %s", err)
- }
+ require.NoErrorf(t, err, "Leader() returned non nil err")
// Recreate the election
e = concurrency.ResumeElection(s, prefix,
@@ -74,7 +72,7 @@ func TestResumeElection(t *testing.T) {
if string(resp.Kvs[0].Value) == "candidate1" {
continue
}
- respChan <- &resp
+ respChan <- resp
return
}
t.Error("Observe() channel closed prematurely")
@@ -86,19 +84,13 @@ func TestResumeElection(t *testing.T) {
// put some random data to generate a change event, this put should be
// ignored by Observe() because it is not under the election prefix.
_, err = cli.Put(ctx, "foo", "bar")
- if err != nil {
- t.Fatalf("Put('foo') returned non nil err: %s", err)
- }
+ require.NoErrorf(t, err, "Put('foo') returned non nil err")
// resign as leader
- if err := e.Resign(ctx); err != nil {
- t.Fatalf("Resign() returned non nil err: %s", err)
- }
+ require.NoErrorf(t, e.Resign(ctx), "Resign() returned non nil err")
// elect a different candidate
- if err := e.Campaign(ctx, "candidate2"); err != nil {
- t.Fatalf("Campaign() returned non nil err: %s", err)
- }
+ require.NoErrorf(t, e.Campaign(ctx, "candidate2"), "Campaign() returned non nil err")
// wait for observed leader change
resp := <-respChan
diff --git a/tests/integration/clientv3/concurrency/mutex_test.go b/tests/integration/clientv3/concurrency/mutex_test.go
index c9a3b7aaecb5..2f45fd6882e2 100644
--- a/tests/integration/clientv3/concurrency/mutex_test.go
+++ b/tests/integration/clientv3/concurrency/mutex_test.go
@@ -15,7 +15,6 @@
package concurrency_test
import (
- "context"
"errors"
"testing"
@@ -23,11 +22,11 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMutexLockSessionExpired(t *testing.T) {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
require.NoError(t, err)
defer cli.Close()
@@ -42,27 +41,27 @@ func TestMutexLockSessionExpired(t *testing.T) {
m2 := concurrency.NewMutex(s2, "/my-lock/")
// acquire lock for s1
- require.NoError(t, m1.Lock(context.TODO()))
+ require.NoError(t, m1.Lock(t.Context()))
m2Locked := make(chan struct{})
var err2 error
go func() {
defer close(m2Locked)
// m2 blocks since m1 already acquired lock /my-lock/
- if err2 = m2.Lock(context.TODO()); err2 == nil {
+ if err2 = m2.Lock(t.Context()); err2 == nil {
t.Error("expect session expired error")
}
}()
// revoke the session of m2 before unlock m1
require.NoError(t, s2.Close())
- require.NoError(t, m1.Unlock(context.TODO()))
+ require.NoError(t, m1.Unlock(t.Context()))
<-m2Locked
}
func TestMutexUnlock(t *testing.T) {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
require.NoError(t, err)
defer cli.Close()
@@ -71,17 +70,17 @@ func TestMutexUnlock(t *testing.T) {
defer s1.Close()
m1 := concurrency.NewMutex(s1, "/my-lock/")
- err = m1.Unlock(context.TODO())
+ err = m1.Unlock(t.Context())
require.Errorf(t, err, "expect lock released error")
if !errors.Is(err, concurrency.ErrLockReleased) {
t.Fatal(err)
}
- require.NoError(t, m1.Lock(context.TODO()))
+ require.NoError(t, m1.Lock(t.Context()))
- require.NoError(t, m1.Unlock(context.TODO()))
+ require.NoError(t, m1.Unlock(t.Context()))
- err = m1.Unlock(context.TODO())
+ err = m1.Unlock(t.Context())
if err == nil {
t.Fatal("expect lock released error")
}
diff --git a/tests/integration/clientv3/concurrency/session_test.go b/tests/integration/clientv3/concurrency/session_test.go
index e7bcc563889e..646044726a28 100644
--- a/tests/integration/clientv3/concurrency/session_test.go
+++ b/tests/integration/clientv3/concurrency/session_test.go
@@ -24,14 +24,14 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestSessionOptions(t *testing.T) {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
require.NoError(t, err)
defer cli.Close()
- lease, err := cli.Grant(context.Background(), 100)
+ lease, err := cli.Grant(t.Context(), 100)
require.NoError(t, err)
s, err := concurrency.NewSession(cli, concurrency.WithLease(lease.ID))
require.NoError(t, err)
@@ -47,7 +47,7 @@ func TestSessionOptions(t *testing.T) {
}
func TestSessionTTLOptions(t *testing.T) {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
require.NoError(t, err)
defer cli.Close()
@@ -58,7 +58,7 @@ func TestSessionTTLOptions(t *testing.T) {
leaseID := s.Lease()
// TTL retrieved should be less than the set TTL, but not equal to default:60 or exprired:-1
- resp, err := cli.Lease.TimeToLive(context.Background(), leaseID)
+ resp, err := cli.Lease.TimeToLive(t.Context(), leaseID)
if err != nil {
t.Log(err)
}
@@ -74,10 +74,10 @@ func TestSessionTTLOptions(t *testing.T) {
}
func TestSessionCtx(t *testing.T) {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: exampleEndpoints()})
require.NoError(t, err)
defer cli.Close()
- lease, err := cli.Grant(context.Background(), 100)
+ lease, err := cli.Grant(t.Context(), 100)
require.NoError(t, err)
s, err := concurrency.NewSession(cli, concurrency.WithLease(lease.ID))
require.NoError(t, err)
diff --git a/tests/integration/clientv3/connectivity/black_hole_test.go b/tests/integration/clientv3/connectivity/black_hole_test.go
index 00b7849ea565..3b04acd8d29d 100644
--- a/tests/integration/clientv3/connectivity/black_hole_test.go
+++ b/tests/integration/clientv3/connectivity/black_hole_test.go
@@ -23,11 +23,10 @@ import (
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
clientv3test "go.etcd.io/etcd/tests/v3/integration/clientv3"
)
@@ -35,9 +34,9 @@ import (
// blackholed endpoint, client balancer switches to healthy one.
// TODO: test server-to-client keepalive ping
func TestBalancerUnderBlackholeKeepAliveWatch(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 2,
GRPCKeepAliveMinTime: time.Millisecond, // avoid too_many_pings
UseBridge: true,
@@ -49,7 +48,6 @@ func TestBalancerUnderBlackholeKeepAliveWatch(t *testing.T) {
ccfg := clientv3.Config{
Endpoints: []string{eps[0]},
DialTimeout: time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
DialKeepAliveTime: time.Second,
DialKeepAliveTimeout: 500 * time.Millisecond,
}
@@ -60,16 +58,15 @@ func TestBalancerUnderBlackholeKeepAliveWatch(t *testing.T) {
// TODO: only send healthy endpoint to gRPC so gRPC wont waste time to
// dial for unhealthy endpoint.
// then we can reduce 3s to 1s.
- timeout := pingInterval + integration2.RequestWaitTimeout
+ timeout := pingInterval + integration.RequestWaitTimeout
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
- wch := cli.Watch(context.Background(), "foo", clientv3.WithCreatedNotify())
- if _, ok := <-wch; !ok {
- t.Fatalf("watch failed on creation")
- }
+ wch := cli.Watch(t.Context(), "foo", clientv3.WithCreatedNotify())
+ _, ok := <-wch
+ require.Truef(t, ok, "watch failed on creation")
// endpoint can switch to eps[1] when it detects the failure of eps[0]
cli.SetEndpoints(eps...)
@@ -79,7 +76,7 @@ func TestBalancerUnderBlackholeKeepAliveWatch(t *testing.T) {
clus.Members[0].Bridge().Blackhole()
- _, err = clus.Client(1).Put(context.TODO(), "foo", "bar")
+ _, err = clus.Client(1).Put(t.Context(), "foo", "bar")
require.NoError(t, err)
select {
case <-wch:
@@ -95,9 +92,9 @@ func TestBalancerUnderBlackholeKeepAliveWatch(t *testing.T) {
clus.Members[1].Bridge().Blackhole()
// make sure client[0] can connect to eps[0] after remove the blackhole.
- _, err = clus.Client(0).Get(context.TODO(), "foo")
+ _, err = clus.Client(0).Get(t.Context(), "foo")
require.NoError(t, err)
- _, err = clus.Client(0).Put(context.TODO(), "foo", "bar1")
+ _, err = clus.Client(0).Put(t.Context(), "foo", "bar1")
require.NoError(t, err)
select {
@@ -163,9 +160,9 @@ func TestBalancerUnderBlackholeNoKeepAliveSerializableGet(t *testing.T) {
// testBalancerUnderBlackholeNoKeepAlive ensures that first request to blackholed endpoint
// fails due to context timeout, but succeeds on next try, with endpoint switch.
func testBalancerUnderBlackholeNoKeepAlive(t *testing.T, op func(*clientv3.Client, context.Context) error) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 2,
UseBridge: true,
})
@@ -176,9 +173,8 @@ func testBalancerUnderBlackholeNoKeepAlive(t *testing.T, op func(*clientv3.Clien
ccfg := clientv3.Config{
Endpoints: []string{eps[0]},
DialTimeout: 1 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
@@ -197,7 +193,7 @@ func testBalancerUnderBlackholeNoKeepAlive(t *testing.T, op func(*clientv3.Clien
// TODO: first operation can succeed
// when gRPC supports better retry on non-delivered request
for i := 0; i < 5; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second*5)
err = op(cli, ctx)
cancel()
if err == nil {
diff --git a/tests/integration/clientv3/connectivity/dial_test.go b/tests/integration/clientv3/connectivity/dial_test.go
index 54556d0f8ddd..f2d9127752e4 100644
--- a/tests/integration/clientv3/connectivity/dial_test.go
+++ b/tests/integration/clientv3/connectivity/dial_test.go
@@ -22,12 +22,11 @@ import (
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
"go.etcd.io/etcd/tests/v3/framework/testutils"
clientv3test "go.etcd.io/etcd/tests/v3/integration/clientv3"
)
@@ -48,46 +47,51 @@ var (
}
)
-// TestDialTLSExpired tests client with expired certs fails to dial.
+// TestDialTLSExpired tests client with expired certs fails the read request.
func TestDialTLSExpired(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, PeerTLS: &testTLSInfo, ClientTLS: &testTLSInfo})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, PeerTLS: &testTLSInfo, ClientTLS: &testTLSInfo})
defer clus.Terminate(t)
tls, err := testTLSInfoExpired.ClientConfig()
require.NoError(t, err)
// expect remote errors "tls: bad certificate"
- _, err = integration2.NewClient(t, clientv3.Config{
+ c, err := integration.NewClient(t, clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: 3 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
TLS: tls,
})
- if !clientv3test.IsClientTimeout(err) {
- t.Fatalf("expected dial timeout error, got %v", err)
- }
+ require.NoError(t, err)
+ defer func() {
+ require.NoError(t, c.Close())
+ }()
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ _, rerr := c.Get(ctx, "foo")
+ cancel()
+ require.Truef(t, clientv3test.IsClientTimeout(rerr), "expected get timeout error")
}
-// TestDialTLSNoConfig ensures the client fails to dial / times out
+// TestDialTLSNoConfig ensures the client fails to range / times out
// when TLS endpoints (https, unixs) are given but no tls config.
func TestDialTLSNoConfig(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, ClientTLS: &testTLSInfo})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, ClientTLS: &testTLSInfo})
defer clus.Terminate(t)
// expect "signed by unknown authority"
- c, err := integration2.NewClient(t, clientv3.Config{
+ c, err := integration.NewClient(t, clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
})
+ require.NoError(t, err)
defer func() {
- if c != nil {
- c.Close()
- }
+ require.NoError(t, c.Close())
}()
- if !clientv3test.IsClientTimeout(err) {
- t.Fatalf("expected dial timeout error, got %v", err)
- }
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ _, rerr := c.Get(ctx, "foo")
+ cancel()
+ require.Truef(t, clientv3test.IsClientTimeout(rerr), "expected get timeout error")
}
// TestDialSetEndpointsBeforeFail ensures SetEndpoints can replace unavailable
@@ -102,8 +106,8 @@ func TestDialSetEndpointsAfterFail(t *testing.T) {
// testDialSetEndpoints ensures SetEndpoints can replace unavailable endpoints with available ones.
func testDialSetEndpoints(t *testing.T, setBefore bool) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// get endpoint list
@@ -116,9 +120,8 @@ func testDialSetEndpoints(t *testing.T, setBefore bool) {
cfg := clientv3.Config{
Endpoints: []string{eps[toKill]},
DialTimeout: 1 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer cli.Close()
@@ -133,7 +136,7 @@ func testDialSetEndpoints(t *testing.T, setBefore bool) {
cli.SetEndpoints(eps[toKill%3], eps[(toKill+1)%3])
}
time.Sleep(time.Second * 2)
- ctx, cancel := context.WithTimeout(context.Background(), integration2.RequestWaitTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestWaitTimeout)
_, err = cli.Get(ctx, "foo", clientv3.WithSerializable())
require.NoError(t, err)
cancel()
@@ -142,8 +145,8 @@ func testDialSetEndpoints(t *testing.T, setBefore bool) {
// TestSwitchSetEndpoints ensures SetEndpoints can switch one endpoint
// with a new one that doesn't include original endpoint.
func TestSwitchSetEndpoints(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// get non partitioned members endpoints
@@ -154,25 +157,24 @@ func TestSwitchSetEndpoints(t *testing.T) {
cli.SetEndpoints(eps...)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
_, err := cli.Get(ctx, "foo")
require.NoError(t, err)
}
func TestRejectOldCluster(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// 2 endpoints to test multi-endpoint Status
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2})
defer clus.Terminate(t)
cfg := clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL, clus.Members[1].GRPCURL},
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
RejectOldCluster: true,
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
cli.Close()
}
@@ -180,8 +182,8 @@ func TestRejectOldCluster(t *testing.T) {
// TestDialForeignEndpoint checks an endpoint that is not registered
// with the balancer can be dialed.
func TestDialForeignEndpoint(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2})
defer clus.Terminate(t)
conn, err := clus.Client(0).Dial(clus.Client(1).Endpoints()[0])
@@ -191,7 +193,7 @@ func TestDialForeignEndpoint(t *testing.T) {
// grpc can return a lazy connection that's not connected yet; confirm
// that it can communicate with the cluster.
kvc := clientv3.NewKVFromKVClient(pb.NewKVClient(conn), clus.Client(0))
- ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
_, gerr := kvc.Get(ctx, "abc")
require.NoError(t, gerr)
@@ -200,12 +202,12 @@ func TestDialForeignEndpoint(t *testing.T) {
// TestSetEndpointAndPut checks that a Put following a SetEndpoints
// to a working endpoint will always succeed.
func TestSetEndpointAndPut(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2})
defer clus.Terminate(t)
clus.Client(1).SetEndpoints(clus.Members[0].GRPCURL)
- _, err := clus.Client(1).Put(context.TODO(), "foo", "bar")
+ _, err := clus.Client(1).Put(t.Context(), "foo", "bar")
if err != nil && !strings.Contains(err.Error(), "closing") {
t.Fatal(err)
}
diff --git a/tests/integration/clientv3/connectivity/main_test.go b/tests/integration/clientv3/connectivity/main_test.go
index 39a188823da7..fb46e5a05da7 100644
--- a/tests/integration/clientv3/connectivity/main_test.go
+++ b/tests/integration/clientv3/connectivity/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 connectivity
diff --git a/tests/integration/clientv3/connectivity/network_partition_test.go b/tests/integration/clientv3/connectivity/network_partition_test.go
index 557cdb0b31e8..e92151d2b495 100644
--- a/tests/integration/clientv3/connectivity/network_partition_test.go
+++ b/tests/integration/clientv3/connectivity/network_partition_test.go
@@ -23,12 +23,11 @@ import (
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
clientv3test "go.etcd.io/etcd/tests/v3/integration/clientv3"
)
@@ -109,9 +108,9 @@ func TestBalancerUnderNetworkPartitionSerializableGet(t *testing.T) {
}
func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, context.Context) error, timeout time.Duration) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -122,9 +121,8 @@ func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, c
ccfg := clientv3.Config{
Endpoints: []string{eps[0]},
DialTimeout: 3 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
// wait for eps[0] to be pinned
@@ -136,7 +134,7 @@ func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, c
clus.Members[0].InjectPartition(t, clus.Members[1:]...)
for i := 0; i < 5; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ ctx, cancel := context.WithTimeout(t.Context(), timeout)
err = op(cli, ctx)
t.Logf("Op returned error: %v", err)
t.Log("Cancelling...")
@@ -162,9 +160,9 @@ func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, c
// switches endpoint when leader fails and linearizable get requests returns
// "etcdserver: request timed out".
func TestBalancerUnderNetworkPartitionLinearizableGetLeaderElection(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -174,10 +172,9 @@ func TestBalancerUnderNetworkPartitionLinearizableGetLeaderElection(t *testing.T
timeout := 3 * clus.Members[(lead+1)%2].ServerConfig.ReqTimeout()
- cli, err := integration2.NewClient(t, clientv3.Config{
+ cli, err := integration.NewClient(t, clientv3.Config{
Endpoints: []string{eps[(lead+1)%2]},
DialTimeout: 2 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
})
require.NoError(t, err)
defer cli.Close()
@@ -191,7 +188,7 @@ func TestBalancerUnderNetworkPartitionLinearizableGetLeaderElection(t *testing.T
// expects balancer to round robin to leader within two attempts
for i := 0; i < 2; i++ {
- ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+ ctx, cancel := context.WithTimeout(t.Context(), timeout)
_, err = cli.Get(ctx, "a")
cancel()
if err == nil {
@@ -212,9 +209,9 @@ func TestBalancerUnderNetworkPartitionWatchFollower(t *testing.T) {
// testBalancerUnderNetworkPartitionWatch ensures watch stream
// to a partitioned node be closed when context requires leader.
func testBalancerUnderNetworkPartitionWatch(t *testing.T, isolateLeader bool) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -227,7 +224,7 @@ func testBalancerUnderNetworkPartitionWatch(t *testing.T, isolateLeader bool) {
}
// pin eps[target]
- watchCli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[target]}})
+ watchCli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[target]}})
require.NoError(t, err)
t.Logf("watchCli created to: %v", target)
defer watchCli.Close()
@@ -240,10 +237,10 @@ func testBalancerUnderNetworkPartitionWatch(t *testing.T, isolateLeader bool) {
// under the cover to other available eps, but expose the failure to the
// caller (test assertion).
- wch := watchCli.Watch(clientv3.WithRequireLeader(context.Background()), "foo", clientv3.WithCreatedNotify())
+ wch := watchCli.Watch(clientv3.WithRequireLeader(t.Context()), "foo", clientv3.WithCreatedNotify())
select {
case <-wch:
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("took too long to create watch")
}
@@ -260,18 +257,16 @@ func testBalancerUnderNetworkPartitionWatch(t *testing.T, isolateLeader bool) {
if len(ev.Events) != 0 {
t.Fatal("expected no event")
}
- if err = ev.Err(); !errors.Is(err, rpctypes.ErrNoLeader) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrNoLeader, err)
- }
- case <-time.After(integration2.RequestWaitTimeout): // enough time to detect leader lost
+ require.ErrorIs(t, ev.Err(), rpctypes.ErrNoLeader)
+ case <-time.After(integration.RequestWaitTimeout): // enough time to detect leader lost
t.Fatal("took too long to detect leader lost")
}
}
func TestDropReadUnderNetworkPartition(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -281,9 +276,8 @@ func TestDropReadUnderNetworkPartition(t *testing.T) {
ccfg := clientv3.Config{
Endpoints: eps,
DialTimeout: 10 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
@@ -299,15 +293,13 @@ func TestDropReadUnderNetworkPartition(t *testing.T) {
clus.Members[leaderIndex].InjectPartition(t, clus.Members[(leaderIndex+1)%3], clus.Members[(leaderIndex+2)%3])
kvc := clientv3.NewKVFromKVClient(pb.NewKVClient(conn), nil)
- ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
_, err = kvc.Get(ctx, "a")
cancel()
- if !errors.Is(err, rpctypes.ErrLeaderChanged) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrLeaderChanged, err)
- }
+ require.ErrorIsf(t, err, rpctypes.ErrLeaderChanged, "expected %v, got %v", rpctypes.ErrLeaderChanged, err)
for i := 0; i < 5; i++ {
- ctx, cancel = context.WithTimeout(context.TODO(), 10*time.Second)
+ ctx, cancel = context.WithTimeout(t.Context(), 10*time.Second)
_, err = kvc.Get(ctx, "a")
cancel()
if err != nil {
diff --git a/tests/integration/clientv3/connectivity/server_shutdown_test.go b/tests/integration/clientv3/connectivity/server_shutdown_test.go
index 9e44ea9f9068..3fc338a66318 100644
--- a/tests/integration/clientv3/connectivity/server_shutdown_test.go
+++ b/tests/integration/clientv3/connectivity/server_shutdown_test.go
@@ -26,16 +26,16 @@ import (
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
clientv3test "go.etcd.io/etcd/tests/v3/integration/clientv3"
)
// TestBalancerUnderServerShutdownWatch expects that watch client
// switch its endpoints when the member of the pinned endpoint fails.
func TestBalancerUnderServerShutdownWatch(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
UseBridge: true,
})
@@ -46,7 +46,7 @@ func TestBalancerUnderServerShutdownWatch(t *testing.T) {
lead := clus.WaitLeader(t)
// pin eps[lead]
- watchCli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[lead]}})
+ watchCli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[lead]}})
require.NoError(t, err)
defer watchCli.Close()
@@ -58,10 +58,10 @@ func TestBalancerUnderServerShutdownWatch(t *testing.T) {
watchCli.SetEndpoints(eps...)
key, val := "foo", "bar"
- wch := watchCli.Watch(context.Background(), key, clientv3.WithCreatedNotify())
+ wch := watchCli.Watch(t.Context(), key, clientv3.WithCreatedNotify())
select {
case <-wch:
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("took too long to create watch")
}
@@ -90,11 +90,11 @@ func TestBalancerUnderServerShutdownWatch(t *testing.T) {
clus.Members[lead].Terminate(t)
// writes to eps[lead+1]
- putCli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[(lead+1)%3]}})
+ putCli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[(lead+1)%3]}})
require.NoError(t, err)
defer putCli.Close()
for {
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
_, err = putCli.Put(ctx, key, val)
cancel()
if err == nil {
@@ -141,9 +141,9 @@ func TestBalancerUnderServerShutdownTxn(t *testing.T) {
// the pinned endpoint is shut down, the balancer switches its endpoints
// and all subsequent put/delete/txn requests succeed with new endpoints.
func testBalancerUnderServerShutdownMutable(t *testing.T, op func(*clientv3.Client, context.Context) error) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -151,7 +151,7 @@ func testBalancerUnderServerShutdownMutable(t *testing.T, op func(*clientv3.Clie
eps := []string{clus.Members[0].GRPCURL, clus.Members[1].GRPCURL, clus.Members[2].GRPCURL}
// pin eps[0]
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[0]}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[0]}})
require.NoError(t, err)
defer cli.Close()
@@ -170,7 +170,7 @@ func testBalancerUnderServerShutdownMutable(t *testing.T, op func(*clientv3.Clie
// TODO: remove this (expose client connection state?)
time.Sleep(time.Second)
- cctx, ccancel := context.WithTimeout(context.Background(), time.Second)
+ cctx, ccancel := context.WithTimeout(t.Context(), time.Second)
err = op(cli, cctx)
ccancel()
require.NoError(t, err)
@@ -194,9 +194,9 @@ func TestBalancerUnderServerShutdownGetSerializable(t *testing.T) {
// the pinned endpoint is shut down, the balancer switches its endpoints
// and all subsequent range requests succeed with new endpoints.
func testBalancerUnderServerShutdownImmutable(t *testing.T, op func(*clientv3.Client, context.Context) error, timeout time.Duration) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
Size: 3,
})
defer clus.Terminate(t)
@@ -204,7 +204,7 @@ func testBalancerUnderServerShutdownImmutable(t *testing.T, op func(*clientv3.Cl
eps := []string{clus.Members[0].GRPCURL, clus.Members[1].GRPCURL, clus.Members[2].GRPCURL}
// pin eps[0]
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[0]}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[0]}})
if err != nil {
t.Errorf("failed to create client: %v", err)
}
@@ -222,7 +222,7 @@ func testBalancerUnderServerShutdownImmutable(t *testing.T, op func(*clientv3.Cl
// switched to others when eps[0] was explicitly shut down
// and following request should succeed
- cctx, ccancel := context.WithTimeout(context.Background(), timeout)
+ cctx, ccancel := context.WithTimeout(t.Context(), timeout)
err = op(cli, cctx)
ccancel()
if err != nil {
@@ -266,9 +266,9 @@ type pinTestOpt struct {
// testBalancerUnderServerStopInflightRangeOnRestart expects
// inflight range request reconnects on server restart.
func testBalancerUnderServerStopInflightRangeOnRestart(t *testing.T, linearizable bool, opt pinTestOpt) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cfg := &integration2.ClusterConfig{
+ cfg := &integration.ClusterConfig{
Size: 2,
UseBridge: true,
}
@@ -276,7 +276,7 @@ func testBalancerUnderServerStopInflightRangeOnRestart(t *testing.T, linearizabl
cfg.Size = 3
}
- clus := integration2.NewCluster(t, cfg)
+ clus := integration.NewCluster(t, cfg)
defer clus.Terminate(t)
eps := []string{clus.Members[0].GRPCURL, clus.Members[1].GRPCURL}
if linearizable {
@@ -291,7 +291,7 @@ func testBalancerUnderServerStopInflightRangeOnRestart(t *testing.T, linearizabl
}
// pin eps[target]
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{eps[target]}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{eps[target]}})
if err != nil {
t.Errorf("failed to create client: %v", err)
}
@@ -329,7 +329,7 @@ func testBalancerUnderServerStopInflightRangeOnRestart(t *testing.T, linearizabl
donec, readyc := make(chan struct{}), make(chan struct{}, 1)
go func() {
defer close(donec)
- ctx, cancel := context.WithTimeout(context.TODO(), clientTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), clientTimeout)
readyc <- struct{}{}
// TODO: The new grpc load balancer will not pin to an endpoint
@@ -352,7 +352,7 @@ func testBalancerUnderServerStopInflightRangeOnRestart(t *testing.T, linearizabl
clus.Members[target].Restart(t)
select {
- case <-time.After(clientTimeout + integration2.RequestWaitTimeout):
+ case <-time.After(clientTimeout + integration.RequestWaitTimeout):
t.Fatalf("timed out waiting for Get [linearizable: %v, opt: %+v]", linearizable, opt)
case <-donec:
}
diff --git a/tests/integration/clientv3/examples/example_cluster_test.go b/tests/integration/clientv3/examples/example_cluster_test.go
index 1d2da78c777e..3a83e8a997fc 100644
--- a/tests/integration/clientv3/examples/example_cluster_test.go
+++ b/tests/integration/clientv3/examples/example_cluster_test.go
@@ -22,12 +22,12 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockCluster_memberList() {
+func mockClusterMemberList() {
fmt.Println("members: 3")
}
func ExampleCluster_memberList() {
- forUnitTestsRunInMockedContext(mockCluster_memberList, func() {
+ forUnitTestsRunInMockedContext(mockClusterMemberList, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -46,13 +46,13 @@ func ExampleCluster_memberList() {
// Output: members: 3
}
-func mockCluster_memberAdd() {
+func mockClusterMemberAdd() {
fmt.Println("added member.PeerURLs: [http://localhost:32380]")
fmt.Println("members count: 4")
}
func ExampleCluster_memberAdd() {
- forUnitTestsRunInMockedContext(mockCluster_memberAdd, func() {
+ forUnitTestsRunInMockedContext(mockClusterMemberAdd, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -81,13 +81,13 @@ func ExampleCluster_memberAdd() {
// members count: 4
}
-func mockCluster_memberAddAsLearner() {
+func mockClusterMemberAddAsLearner() {
fmt.Println("members count: 4")
fmt.Println("added member.IsLearner: true")
}
func ExampleCluster_memberAddAsLearner() {
- forUnitTestsRunInMockedContext(mockCluster_memberAddAsLearner, func() {
+ forUnitTestsRunInMockedContext(mockClusterMemberAddAsLearner, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -116,10 +116,10 @@ func ExampleCluster_memberAddAsLearner() {
// added member.IsLearner: true
}
-func mockCluster_memberRemove() {}
+func mockClusterMemberRemove() {}
func ExampleCluster_memberRemove() {
- forUnitTestsRunInMockedContext(mockCluster_memberRemove, func() {
+ forUnitTestsRunInMockedContext(mockClusterMemberRemove, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -147,10 +147,10 @@ func ExampleCluster_memberRemove() {
})
}
-func mockCluster_memberUpdate() {}
+func mockClusterMemberUpdate() {}
func ExampleCluster_memberUpdate() {
- forUnitTestsRunInMockedContext(mockCluster_memberUpdate, func() {
+ forUnitTestsRunInMockedContext(mockClusterMemberUpdate, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
diff --git a/tests/integration/clientv3/examples/example_kv_test.go b/tests/integration/clientv3/examples/example_kv_test.go
index e4fa4bf5f441..6c4b1c1bfc42 100644
--- a/tests/integration/clientv3/examples/example_kv_test.go
+++ b/tests/integration/clientv3/examples/example_kv_test.go
@@ -16,6 +16,7 @@ package clientv3_test
import (
"context"
+ "errors"
"fmt"
"log"
@@ -23,10 +24,10 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockKV_put() {}
+func mockKVPut() {}
func ExampleKV_put() {
- forUnitTestsRunInMockedContext(mockKV_put, func() {
+ forUnitTestsRunInMockedContext(mockKVPut, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -46,12 +47,12 @@ func ExampleKV_put() {
// Output:
}
-func mockKV_putErrorHandling() {
+func mockKVPutErrorHandling() {
fmt.Println("client-side error: etcdserver: key is not provided")
}
func ExampleKV_putErrorHandling() {
- forUnitTestsRunInMockedContext(mockKV_putErrorHandling, func() {
+ forUnitTestsRunInMockedContext(mockKVPutErrorHandling, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -65,14 +66,13 @@ func ExampleKV_putErrorHandling() {
_, err = cli.Put(ctx, "", "sample_value")
cancel()
if err != nil {
- switch err {
- case context.Canceled:
+ if errors.Is(err, context.Canceled) {
fmt.Printf("ctx is canceled by another routine: %v\n", err)
- case context.DeadlineExceeded:
+ } else if errors.Is(err, context.DeadlineExceeded) {
fmt.Printf("ctx is attached with a deadline is exceeded: %v\n", err)
- case rpctypes.ErrEmptyKey:
+ } else if errors.Is(err, rpctypes.ErrEmptyKey) {
fmt.Printf("client-side error: %v\n", err)
- default:
+ } else {
fmt.Printf("bad cluster endpoints, which are not etcd servers: %v\n", err)
}
}
@@ -80,12 +80,12 @@ func ExampleKV_putErrorHandling() {
// Output: client-side error: etcdserver: key is not provided
}
-func mockKV_get() {
+func mockKVGet() {
fmt.Println("foo : bar")
}
func ExampleKV_get() {
- forUnitTestsRunInMockedContext(mockKV_get, func() {
+ forUnitTestsRunInMockedContext(mockKVGet, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -113,12 +113,12 @@ func ExampleKV_get() {
// Output: foo : bar
}
-func mockKV_getWithRev() {
+func mockKVGetWithRev() {
fmt.Println("foo : bar1")
}
func ExampleKV_getWithRev() {
- forUnitTestsRunInMockedContext(mockKV_getWithRev, func() {
+ forUnitTestsRunInMockedContext(mockKVGetWithRev, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -150,14 +150,14 @@ func ExampleKV_getWithRev() {
// Output: foo : bar1
}
-func mockKV_getSortedPrefix() {
+func mockKVGetSortedPrefix() {
fmt.Println(`key_2 : value`)
fmt.Println(`key_1 : value`)
fmt.Println(`key_0 : value`)
}
func ExampleKV_getSortedPrefix() {
- forUnitTestsRunInMockedContext(mockKV_getSortedPrefix, func() {
+ forUnitTestsRunInMockedContext(mockKVGetSortedPrefix, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -192,12 +192,116 @@ func ExampleKV_getSortedPrefix() {
// key_0 : value
}
-func mockKV_delete() {
+func mockKVGetStream() {
+ fmt.Println("key_0 : value")
+ fmt.Println("key_1 : value")
+ fmt.Println("key_2 : value")
+ fmt.Println("count: 3, more: false")
+}
+
+func ExampleKV_getStream() {
+ forUnitTestsRunInMockedContext(mockKVGetStream, func() {
+ cli, err := clientv3.New(clientv3.Config{
+ Endpoints: exampleEndpoints(),
+ DialTimeout: dialTimeout,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer cli.Close()
+
+ for i := 0; i < 3; i++ {
+ ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
+ _, err = cli.Put(ctx, fmt.Sprintf("key_%d", i), "value")
+ cancel()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
+ defer cancel()
+ stream, err := cli.GetStream(ctx, "key", clientv3.WithPrefix())
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Header, More, and Count are populated only on the final chunk, and
+ // only when the stream completes without error. Track the latest
+ // chunk to read them, the same way they appear on a unary Get response.
+ var last clientv3.RangeStreamResponse
+ for chunk := range stream {
+ if err := chunk.Err(); err != nil {
+ log.Fatal(err)
+ }
+ for _, ev := range chunk.Kvs {
+ fmt.Printf("%s : %s\n", ev.Key, ev.Value)
+ }
+ last = chunk
+ }
+ fmt.Printf("count: %d, more: %v\n", last.Count, last.More)
+ })
+ // Output:
+ // key_0 : value
+ // key_1 : value
+ // key_2 : value
+ // count: 3, more: false
+}
+
+func mockKVGetStreamToGetResponse() {
+ fmt.Println("count: 3")
+ fmt.Println("key_0 : value")
+ fmt.Println("key_1 : value")
+ fmt.Println("key_2 : value")
+}
+
+func ExampleKV_getStreamToGetResponse() {
+ forUnitTestsRunInMockedContext(mockKVGetStreamToGetResponse, func() {
+ cli, err := clientv3.New(clientv3.Config{
+ Endpoints: exampleEndpoints(),
+ DialTimeout: dialTimeout,
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer cli.Close()
+
+ for i := 0; i < 3; i++ {
+ ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
+ _, err = cli.Put(ctx, fmt.Sprintf("key_%d", i), "value")
+ cancel()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
+ defer cancel()
+ stream, err := cli.GetStream(ctx, "key", clientv3.WithPrefix())
+ if err != nil {
+ log.Fatal(err)
+ }
+ resp, err := clientv3.GetStreamToGetResponse(stream)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("count: %d\n", resp.Count)
+ for _, ev := range resp.Kvs {
+ fmt.Printf("%s : %s\n", ev.Key, ev.Value)
+ }
+ })
+ // Output:
+ // count: 3
+ // key_0 : value
+ // key_1 : value
+ // key_2 : value
+}
+
+func mockKVDelete() {
fmt.Println("Deleted all keys: true")
}
func ExampleKV_delete() {
- forUnitTestsRunInMockedContext(mockKV_delete, func() {
+ forUnitTestsRunInMockedContext(mockKVDelete, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -228,10 +332,10 @@ func ExampleKV_delete() {
// Deleted all keys: true
}
-func mockKV_compact() {}
+func mockKVCompact() {}
func ExampleKV_compact() {
- forUnitTestsRunInMockedContext(mockKV_compact, func() {
+ forUnitTestsRunInMockedContext(mockKVCompact, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -259,12 +363,12 @@ func ExampleKV_compact() {
// Output:
}
-func mockKV_txn() {
+func mockKVTxn() {
fmt.Println("key : XYZ")
}
func ExampleKV_txn() {
- forUnitTestsRunInMockedContext(mockKV_txn, func() {
+ forUnitTestsRunInMockedContext(mockKVTxn, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -306,10 +410,10 @@ func ExampleKV_txn() {
// Output: key : XYZ
}
-func mockKV_do() {}
+func mockKVDo() {}
func ExampleKV_do() {
- forUnitTestsRunInMockedContext(mockKV_do, func() {
+ forUnitTestsRunInMockedContext(mockKVDo, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
diff --git a/tests/integration/clientv3/examples/example_lease_test.go b/tests/integration/clientv3/examples/example_lease_test.go
index b0e6c5ef3668..f55e8314b2d3 100644
--- a/tests/integration/clientv3/examples/example_lease_test.go
+++ b/tests/integration/clientv3/examples/example_lease_test.go
@@ -22,11 +22,11 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockLease_grant() {
+func mockLeaseGrant() {
}
func ExampleLease_grant() {
- forUnitTestsRunInMockedContext(mockLease_grant, func() {
+ forUnitTestsRunInMockedContext(mockLeaseGrant, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -51,12 +51,12 @@ func ExampleLease_grant() {
// Output:
}
-func mockLease_revoke() {
+func mockLeaseRevoke() {
fmt.Println("number of keys: 0")
}
func ExampleLease_revoke() {
- forUnitTestsRunInMockedContext(mockLease_revoke, func() {
+ forUnitTestsRunInMockedContext(mockLeaseRevoke, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -91,12 +91,12 @@ func ExampleLease_revoke() {
// Output: number of keys: 0
}
-func mockLease_keepAlive() {
+func mockLeaseKeepAlive() {
fmt.Println("ttl: 5")
}
func ExampleLease_keepAlive() {
- forUnitTestsRunInMockedContext(mockLease_keepAlive, func() {
+ forUnitTestsRunInMockedContext(mockLeaseKeepAlive, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -132,12 +132,12 @@ func ExampleLease_keepAlive() {
// Output: ttl: 5
}
-func mockLease_keepAliveOnce() {
+func mockLeaseKeepAliveOnce() {
fmt.Println("ttl: 5")
}
func ExampleLease_keepAliveOnce() {
- forUnitTestsRunInMockedContext(mockLease_keepAliveOnce, func() {
+ forUnitTestsRunInMockedContext(mockLeaseKeepAliveOnce, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
diff --git a/tests/integration/clientv3/examples/example_maintenance_test.go b/tests/integration/clientv3/examples/example_maintenance_test.go
index ff545e8de7d5..2c901475a7c0 100644
--- a/tests/integration/clientv3/examples/example_maintenance_test.go
+++ b/tests/integration/clientv3/examples/example_maintenance_test.go
@@ -21,10 +21,10 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockMaintenance_status() {}
+func mockMaintenanceStatus() {}
func ExampleMaintenance_status() {
- forUnitTestsRunInMockedContext(mockMaintenance_status, func() {
+ forUnitTestsRunInMockedContext(mockMaintenanceStatus, func() {
for _, ep := range exampleEndpoints() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{ep},
@@ -44,10 +44,10 @@ func ExampleMaintenance_status() {
// Output:
}
-func mockMaintenance_defragment() {}
+func mockMaintenanceDefragment() {}
func ExampleMaintenance_defragment() {
- forUnitTestsRunInMockedContext(mockMaintenance_defragment, func() {
+ forUnitTestsRunInMockedContext(mockMaintenanceDefragment, func() {
for _, ep := range exampleEndpoints() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{ep},
diff --git a/tests/integration/clientv3/examples/example_metrics_test.go b/tests/integration/clientv3/examples/example_metrics_test.go
index 75b47e53d23e..d467c8281333 100644
--- a/tests/integration/clientv3/examples/example_metrics_test.go
+++ b/tests/integration/clientv3/examples/example_metrics_test.go
@@ -31,12 +31,12 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockClient_metrics() {
+func mockClientMetrics() {
fmt.Println(`grpc_client_started_total{grpc_method="Range",grpc_service="etcdserverpb.KV",grpc_type="unary"} 1`)
}
func ExampleClient_metrics() {
- forUnitTestsRunInMockedContext(mockClient_metrics, func() {
+ forUnitTestsRunInMockedContext(mockClientMetrics, func() {
clientMetrics := grpcprom.NewClientMetrics()
prometheus.Register(clientMetrics)
cli, err := clientv3.New(clientv3.Config{
diff --git a/tests/integration/clientv3/examples/example_test.go b/tests/integration/clientv3/examples/example_test.go
index b9b8be461e7c..166f4fbd2aa6 100644
--- a/tests/integration/clientv3/examples/example_test.go
+++ b/tests/integration/clientv3/examples/example_test.go
@@ -22,10 +22,10 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockConfig_insecure() {}
+func mockConfigInsecure() {}
func ExampleConfig_insecure() {
- forUnitTestsRunInMockedContext(mockConfig_insecure, func() {
+ forUnitTestsRunInMockedContext(mockConfigInsecure, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -46,10 +46,10 @@ func ExampleConfig_insecure() {
// Output:
}
-func mockConfig_withTLS() {}
+func mockConfigWithTLS() {}
func ExampleConfig_withTLS() {
- forUnitTestsRunInMockedContext(mockConfig_withTLS, func() {
+ forUnitTestsRunInMockedContext(mockConfigWithTLS, func() {
tlsInfo := transport.TLSInfo{
CertFile: "/tmp/test-certs/test-name-1.pem",
KeyFile: "/tmp/test-certs/test-name-1-key.pem",
diff --git a/tests/integration/clientv3/examples/example_watch_test.go b/tests/integration/clientv3/examples/example_watch_test.go
index ac44f8ca38dd..c78d1e7f84e4 100644
--- a/tests/integration/clientv3/examples/example_watch_test.go
+++ b/tests/integration/clientv3/examples/example_watch_test.go
@@ -23,12 +23,12 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
)
-func mockWatcher_watch() {
+func mockWatcherWatch() {
fmt.Println(`PUT "foo" : "bar"`)
}
func ExampleWatcher_watch() {
- forUnitTestsRunInMockedContext(mockWatcher_watch, func() {
+ forUnitTestsRunInMockedContext(mockWatcherWatch, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -48,12 +48,12 @@ func ExampleWatcher_watch() {
// PUT "foo" : "bar"
}
-func mockWatcher_watchWithPrefix() {
+func mockWatcherWatchWithPrefix() {
fmt.Println(`PUT "foo1" : "bar"`)
}
func ExampleWatcher_watchWithPrefix() {
- forUnitTestsRunInMockedContext(mockWatcher_watchWithPrefix, func() {
+ forUnitTestsRunInMockedContext(mockWatcherWatchWithPrefix, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -73,14 +73,14 @@ func ExampleWatcher_watchWithPrefix() {
// PUT "foo1" : "bar"
}
-func mockWatcher_watchWithRange() {
+func mockWatcherWatchWithRange() {
fmt.Println(`PUT "foo1" : "bar1"`)
fmt.Println(`PUT "foo2" : "bar2"`)
fmt.Println(`PUT "foo3" : "bar3"`)
}
func ExampleWatcher_watchWithRange() {
- forUnitTestsRunInMockedContext(mockWatcher_watchWithRange, func() {
+ forUnitTestsRunInMockedContext(mockWatcherWatchWithRange, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
@@ -121,12 +121,12 @@ func ExampleWatcher_watchWithRange() {
// PUT "foo3" : "bar3"
}
-func mockWatcher_watchWithProgressNotify() {
+func mockWatcherWatchWithProgressNotify() {
fmt.Println(`wresp.IsProgressNotify: true`)
}
func ExampleWatcher_watchWithProgressNotify() {
- forUnitTestsRunInMockedContext(mockWatcher_watchWithProgressNotify, func() {
+ forUnitTestsRunInMockedContext(mockWatcherWatchWithProgressNotify, func() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: exampleEndpoints(),
DialTimeout: dialTimeout,
diff --git a/tests/integration/clientv3/examples/main_test.go b/tests/integration/clientv3/examples/main_test.go
index 338a1ed64684..5c0a76aa57fd 100644
--- a/tests/integration/clientv3/examples/main_test.go
+++ b/tests/integration/clientv3/examples/main_test.go
@@ -21,7 +21,7 @@ import (
"time"
"go.etcd.io/etcd/client/pkg/v3/testutil"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ framework "go.etcd.io/etcd/tests/v3/framework/integration"
"go.etcd.io/etcd/tests/v3/integration"
)
@@ -31,7 +31,7 @@ const (
)
var lazyCluster = integration.NewLazyClusterWithConfig(
- integration2.ClusterConfig{
+ framework.ClusterConfig{
Size: 3,
WatchProgressNotifyInterval: 200 * time.Millisecond,
DisableStrictReconfigCheck: true,
diff --git a/tests/integration/clientv3/experimental/recipes/v3_barrier_test.go b/tests/integration/clientv3/experimental/recipes/v3_barrier_test.go
index b6dfef385a7a..06b1540a5e1c 100644
--- a/tests/integration/clientv3/experimental/recipes/v3_barrier_test.go
+++ b/tests/integration/clientv3/experimental/recipes/v3_barrier_test.go
@@ -15,40 +15,37 @@
package recipes_test
import (
- "context"
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
clientv3 "go.etcd.io/etcd/client/v3"
recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestBarrierSingleNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
testBarrier(t, 5, func() *clientv3.Client { return clus.Client(0) })
}
func TestBarrierMultiNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
testBarrier(t, 5, func() *clientv3.Client { return clus.RandClient() })
}
func testBarrier(t *testing.T, waiters int, chooseClient func() *clientv3.Client) {
b := recipe.NewBarrier(chooseClient(), "test-barrier")
- if err := b.Hold(); err != nil {
- t.Fatalf("could not hold barrier (%v)", err)
- }
- if err := b.Hold(); err == nil {
- t.Fatalf("able to double-hold barrier")
- }
+ require.NoErrorf(t, b.Hold(), "could not hold barrier")
+ require.Errorf(t, b.Hold(), "able to double-hold barrier")
// put a random key to move the revision forward
- if _, err := chooseClient().Put(context.Background(), "x", ""); err != nil {
+ if _, err := chooseClient().Put(t.Context(), "x", ""); err != nil {
t.Errorf("could not put x (%v)", err)
}
@@ -75,9 +72,7 @@ func testBarrier(t *testing.T, waiters int, chooseClient func() *clientv3.Client
default:
}
- if err := b.Release(); err != nil {
- t.Fatalf("could not release barrier (%v)", err)
- }
+ require.NoErrorf(t, b.Release(), "could not release barrier")
timerC := time.After(time.Duration(waiters*100) * time.Millisecond)
for i := 0; i < waiters; i++ {
@@ -90,8 +85,8 @@ func testBarrier(t *testing.T, waiters int, chooseClient func() *clientv3.Client
}
func TestBarrierWaitNonexistentKey(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
diff --git a/tests/integration/clientv3/experimental/recipes/v3_double_barrier_test.go b/tests/integration/clientv3/experimental/recipes/v3_double_barrier_test.go
index d8f610e66c50..9b1637d05270 100644
--- a/tests/integration/clientv3/experimental/recipes/v3_double_barrier_test.go
+++ b/tests/integration/clientv3/experimental/recipes/v3_double_barrier_test.go
@@ -15,7 +15,6 @@
package recipes_test
import (
- "context"
"errors"
"sync"
"testing"
@@ -27,13 +26,13 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestDoubleBarrier(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
waiters := 10
@@ -73,9 +72,7 @@ func TestDoubleBarrier(t *testing.T) {
default:
}
- if err := b.Enter(); err != nil {
- t.Fatalf("could not enter last barrier (%v)", err)
- }
+ require.NoErrorf(t, b.Enter(), "could not enter last barrier")
timerC := time.After(time.Duration(waiters*100) * time.Millisecond)
for i := 0; i < waiters-1; i++ {
@@ -105,9 +102,9 @@ func TestDoubleBarrier(t *testing.T) {
}
func TestDoubleBarrierTooManyClients(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
waiters := 10
@@ -155,7 +152,7 @@ func TestDoubleBarrierTooManyClients(t *testing.T) {
t.Errorf("Unexcepted error, expected: ErrTooManyClients, got: %v", err)
}
- resp, err := clus.RandClient().Get(context.TODO(), "test-barrier/waiters", clientv3.WithPrefix())
+ resp, err := clus.RandClient().Get(t.Context(), "test-barrier/waiters", clientv3.WithPrefix())
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
@@ -167,9 +164,9 @@ func TestDoubleBarrierTooManyClients(t *testing.T) {
}
func TestDoubleBarrierFailover(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
waiters := 10
diff --git a/tests/integration/clientv3/experimental/recipes/v3_lock_test.go b/tests/integration/clientv3/experimental/recipes/v3_lock_test.go
index 4a802f72cb47..c97bc09d3946 100644
--- a/tests/integration/clientv3/experimental/recipes/v3_lock_test.go
+++ b/tests/integration/clientv3/experimental/recipes/v3_lock_test.go
@@ -29,29 +29,29 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMutexLockSingleNode(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
var clients []*clientv3.Client
- testMutexLock(t, 5, integration2.MakeSingleNodeClients(t, clus, &clients))
- integration2.CloseClients(t, clients)
+ testMutexLock(t, 5, integration.MakeSingleNodeClients(t, clus, &clients))
+ integration.CloseClients(t, clients)
}
func TestMutexLockMultiNode(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
var clients []*clientv3.Client
- testMutexLock(t, 5, integration2.MakeMultiNodeClients(t, clus, &clients))
- integration2.CloseClients(t, clients)
+ testMutexLock(t, 5, integration.MakeMultiNodeClients(t, clus, &clients))
+ integration.CloseClients(t, clients)
}
func testMutexLock(t *testing.T, waiters int, chooseClient func() *clientv3.Client) {
@@ -71,7 +71,7 @@ func testMutexLock(t *testing.T, waiters int, chooseClient func() *clientv3.Clie
return
}
m := concurrency.NewMutex(session, "test-mutex")
- if err := m.Lock(context.TODO()); err != nil {
+ if err := m.Lock(t.Context()); err != nil {
errC <- fmt.Errorf("#%d: failed to wait on lock: %w", i, err)
return
}
@@ -93,36 +93,34 @@ func testMutexLock(t *testing.T, waiters int, chooseClient func() *clientv3.Clie
t.Fatalf("lock %d followers did not wait", i)
default:
}
- if err := m.Unlock(context.TODO()); err != nil {
- t.Fatalf("could not release lock (%v)", err)
- }
+ require.NoErrorf(t, m.Unlock(t.Context()), "could not release lock")
}
}
wg.Wait()
}
func TestMutexTryLockSingleNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
t.Logf("3 nodes cluster created...")
var clients []*clientv3.Client
- testMutexTryLock(t, 5, integration2.MakeSingleNodeClients(t, clus, &clients))
- integration2.CloseClients(t, clients)
+ testMutexTryLock(t, 5, integration.MakeSingleNodeClients(t, clus, &clients))
+ integration.CloseClients(t, clients)
}
func TestMutexTryLockMultiNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
var clients []*clientv3.Client
- testMutexTryLock(t, 5, integration2.MakeMultiNodeClients(t, clus, &clients))
- integration2.CloseClients(t, clients)
+ testMutexTryLock(t, 5, integration.MakeMultiNodeClients(t, clus, &clients))
+ integration.CloseClients(t, clients)
}
func testMutexTryLock(t *testing.T, lockers int, chooseClient func() *clientv3.Client) {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
lockedC := make(chan *concurrency.Mutex)
@@ -174,9 +172,9 @@ func testMutexTryLock(t *testing.T, lockers int, chooseClient func() *clientv3.C
// TestMutexSessionRelock ensures that acquiring the same lock with the same
// session will not result in deadlock.
func TestMutexSessionRelock(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
session, err := concurrency.NewSession(clus.RandClient())
if err != nil {
@@ -184,22 +182,22 @@ func TestMutexSessionRelock(t *testing.T) {
}
m := concurrency.NewMutex(session, "test-mutex")
- require.NoError(t, m.Lock(context.TODO()))
+ require.NoError(t, m.Lock(t.Context()))
m2 := concurrency.NewMutex(session, "test-mutex")
- require.NoError(t, m2.Lock(context.TODO()))
+ require.NoError(t, m2.Lock(t.Context()))
}
// TestMutexWaitsOnCurrentHolder ensures a mutex is only acquired once all
// waiters older than the new owner are gone by testing the case where
// the waiter prior to the acquirer expires before the current holder.
func TestMutexWaitsOnCurrentHolder(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- cctx := context.Background()
+ cctx := t.Context()
cli := clus.Client(0)
@@ -233,9 +231,7 @@ func TestMutexWaitsOnCurrentHolder(t *testing.T) {
t.Fatal("failed to receive watch response")
}
}
- if putCounts != 2 {
- t.Fatalf("expect 2 put events, but got %v", putCounts)
- }
+ require.Equalf(t, 2, putCounts, "expect 2 put events, but got %v", putCounts)
newOwnerSession, err := concurrency.NewSession(cli)
if err != nil {
@@ -250,12 +246,9 @@ func TestMutexWaitsOnCurrentHolder(t *testing.T) {
select {
case wrp := <-wch:
- if len(wrp.Events) != 1 {
- t.Fatalf("expect a event, but got %v events", len(wrp.Events))
- }
- if e := wrp.Events[0]; e.Type != mvccpb.PUT {
- t.Fatalf("expect a put event on prefix test-mutex, but got event type %v", e.Type)
- }
+ require.Lenf(t, wrp.Events, 1, "expect a event, but got %v events", len(wrp.Events))
+ e := wrp.Events[0]
+ require.Equalf(t, mvccpb.Event_PUT, e.Type, "expect a put event on prefix test-mutex, but got event type %v", e.Type)
case <-time.After(time.Second):
t.Fatalf("failed to receive a watch response")
}
@@ -266,12 +259,9 @@ func TestMutexWaitsOnCurrentHolder(t *testing.T) {
// ensures the deletion of victim waiter from server side.
select {
case wrp := <-wch:
- if len(wrp.Events) != 1 {
- t.Fatalf("expect a event, but got %v events", len(wrp.Events))
- }
- if e := wrp.Events[0]; e.Type != mvccpb.DELETE {
- t.Fatalf("expect a delete event on prefix test-mutex, but got event type %v", e.Type)
- }
+ require.Lenf(t, wrp.Events, 1, "expect a event, but got %v events", len(wrp.Events))
+ e := wrp.Events[0]
+ require.Equalf(t, mvccpb.Event_DELETE, e.Type, "expect a delete event on prefix test-mutex, but got event type %v", e.Type)
case <-time.After(time.Second):
t.Fatal("failed to receive a watch response")
}
@@ -298,9 +288,9 @@ func TestMutexWaitsOnCurrentHolder(t *testing.T) {
}
func BenchmarkMutex4Waiters(b *testing.B) {
- integration2.BeforeTest(b)
+ integration.BeforeTest(b)
// XXX switch tests to use TB interface
- clus := integration2.NewCluster(nil, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(nil, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(nil)
for i := 0; i < b.N; i++ {
testMutexLock(nil, 4, func() *clientv3.Client { return clus.RandClient() })
@@ -308,15 +298,15 @@ func BenchmarkMutex4Waiters(b *testing.B) {
}
func TestRWMutexSingleNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
testRWMutex(t, 5, func() *clientv3.Client { return clus.Client(0) })
}
func TestRWMutexMultiNode(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
testRWMutex(t, 5, func() *clientv3.Client { return clus.RandClient() })
}
@@ -357,18 +347,14 @@ func testRWMutex(t *testing.T, waiters int, chooseClient func() *clientv3.Client
t.Fatalf("rlock %d readers did not wait", i)
default:
}
- if err := wl.Unlock(); err != nil {
- t.Fatalf("could not release lock (%v)", err)
- }
+ require.NoErrorf(t, wl.Unlock(), "could not release lock")
case rl := <-rlockedC:
select {
case <-wlockedC:
t.Fatalf("rlock %d writers did not wait", i)
default:
}
- if err := rl.RUnlock(); err != nil {
- t.Fatalf("could not release rlock (%v)", err)
- }
+ require.NoErrorf(t, rl.RUnlock(), "could not release rlock")
}
}
}
diff --git a/tests/integration/clientv3/experimental/recipes/v3_queue_test.go b/tests/integration/clientv3/experimental/recipes/v3_queue_test.go
index 73ed5552fe2a..ce5ed92ee876 100644
--- a/tests/integration/clientv3/experimental/recipes/v3_queue_test.go
+++ b/tests/integration/clientv3/experimental/recipes/v3_queue_test.go
@@ -20,8 +20,10 @@ import (
"sync/atomic"
"testing"
+ "github.com/stretchr/testify/require"
+
recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
const (
@@ -31,9 +33,9 @@ const (
// TestQueueOneReaderOneWriter confirms the queue is FIFO
func TestQueueOneReaderOneWriter(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
done := make(chan struct{})
@@ -57,12 +59,8 @@ func TestQueueOneReaderOneWriter(t *testing.T) {
q := recipe.NewQueue(etcdc, "testq")
for i := 0; i < 5; i++ {
s, err := q.Dequeue()
- if err != nil {
- t.Fatalf("error dequeueing (%v)", err)
- }
- if s != fmt.Sprintf("%d", i) {
- t.Fatalf("expected dequeue value %v, got %v", s, i)
- }
+ require.NoErrorf(t, err, "error dequeueing (%v)", err)
+ require.Equalf(t, s, fmt.Sprintf("%d", i), "expected dequeue value %v, got %v", s, i)
}
}
@@ -80,10 +78,10 @@ func TestQueueManyReaderManyWriter(t *testing.T) {
// BenchmarkQueue benchmarks Queues using many/many readers/writers
func BenchmarkQueue(b *testing.B) {
- integration2.BeforeTest(b)
+ integration.BeforeTest(b)
// XXX switch tests to use TB interface
- clus := integration2.NewCluster(nil, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(nil, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(nil)
for i := 0; i < b.N; i++ {
testQueueNReaderMWriter(nil, manyQueueClients, manyQueueClients)
@@ -92,9 +90,9 @@ func BenchmarkQueue(b *testing.B) {
// TestPrQueueOneReaderOneWriter tests whether priority queues respect priorities.
func TestPrQueueOneReaderOneWriter(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
// write out five items with random priority
@@ -103,32 +101,25 @@ func TestPrQueueOneReaderOneWriter(t *testing.T) {
for i := 0; i < 5; i++ {
// [0, 2] priority for priority collision to test seq keys
pr := uint16(rand.Intn(3))
- if err := q.Enqueue(fmt.Sprintf("%d", pr), pr); err != nil {
- t.Fatalf("error enqueuing (%v)", err)
- }
+ require.NoErrorf(t, q.Enqueue(fmt.Sprintf("%d", pr), pr), "error enqueuing")
}
// read back items; confirm priority order is respected
lastPr := -1
for i := 0; i < 5; i++ {
s, err := q.Dequeue()
- if err != nil {
- t.Fatalf("error dequeueing (%v)", err)
- }
+ require.NoErrorf(t, err, "error dequeueing (%v)", err)
curPr := 0
- if _, err := fmt.Sscanf(s, "%d", &curPr); err != nil {
- t.Fatalf(`error parsing item "%s" (%v)`, s, err)
- }
- if lastPr > curPr {
- t.Fatalf("expected priority %v > %v", curPr, lastPr)
- }
+ _, err = fmt.Sscanf(s, "%d", &curPr)
+ require.NoErrorf(t, err, `error parsing item "%s" (%v)`, s, err)
+ require.LessOrEqualf(t, lastPr, curPr, "expected priority %v > %v", curPr, lastPr)
}
}
func TestPrQueueManyReaderManyWriter(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
rqs := newPriorityQueues(clus, manyQueueClients)
wqs := newPriorityQueues(clus, manyQueueClients)
@@ -137,10 +128,10 @@ func TestPrQueueManyReaderManyWriter(t *testing.T) {
// BenchmarkPrQueueOneReaderOneWriter benchmarks Queues using n/n readers/writers
func BenchmarkPrQueueOneReaderOneWriter(b *testing.B) {
- integration2.BeforeTest(b)
+ integration.BeforeTest(b)
// XXX switch tests to use TB interface
- clus := integration2.NewCluster(nil, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(nil, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(nil)
rqs := newPriorityQueues(clus, 1)
wqs := newPriorityQueues(clus, 1)
@@ -150,13 +141,13 @@ func BenchmarkPrQueueOneReaderOneWriter(b *testing.B) {
}
func testQueueNReaderMWriter(t *testing.T, n int, m int) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
testReadersWriters(t, newQueues(clus, n), newQueues(clus, m))
}
-func newQueues(clus *integration2.Cluster, n int) (qs []testQueue) {
+func newQueues(clus *integration.Cluster, n int) (qs []testQueue) {
for i := 0; i < n; i++ {
etcdc := clus.RandClient()
qs = append(qs, recipe.NewQueue(etcdc, "q"))
@@ -164,7 +155,7 @@ func newQueues(clus *integration2.Cluster, n int) (qs []testQueue) {
return qs
}
-func newPriorityQueues(clus *integration2.Cluster, n int) (qs []testQueue) {
+func newPriorityQueues(clus *integration.Cluster, n int) (qs []testQueue) {
for i := 0; i < n; i++ {
etcdc := clus.RandClient()
q := &flatPriorityQueue{recipe.NewPriorityQueue(etcdc, "prq")}
diff --git a/tests/integration/clientv3/kv_test.go b/tests/integration/clientv3/kv_test.go
index c09f1345c520..2e8eccb6729d 100644
--- a/tests/integration/clientv3/kv_test.go
+++ b/tests/integration/clientv3/kv_test.go
@@ -21,35 +21,36 @@ import (
"fmt"
"os"
"reflect"
+ "slices"
"strconv"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestKVPutError(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
var (
maxReqBytes = 1.5 * 1024 * 1024 // hard coded max in v3_server.go
quota = int64(int(maxReqBytes*1.2) + 8*os.Getpagesize()) // make sure we have enough overhead in backend quota. See discussion in #6486.
)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, QuotaBackendBytes: quota, ClientMaxCallSendMsgSize: 100 * 1024 * 1024})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, QuotaBackendBytes: quota, ClientMaxCallSendMsgSize: 100 * 1024 * 1024})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
_, err := kv.Put(ctx, "", "bar")
if !errors.Is(err, rpctypes.ErrEmptyKey) {
@@ -73,17 +74,17 @@ func TestKVPutError(t *testing.T) {
}
func TestKVPutWithLease(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lapi := clus.RandClient()
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
- lease, err := lapi.Grant(context.Background(), 10)
+ lease, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Fatalf("failed to create lease %v", err)
}
@@ -110,24 +111,24 @@ func TestKVPutWithLease(t *testing.T) {
// TestKVPutWithIgnoreValue ensures that Put with WithIgnoreValue does not clobber the old value.
func TestKVPutWithIgnoreValue(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.RandClient()
- _, err := kv.Put(context.TODO(), "foo", "", clientv3.WithIgnoreValue())
+ _, err := kv.Put(t.Context(), "foo", "", clientv3.WithIgnoreValue())
if !errors.Is(err, rpctypes.ErrKeyNotFound) {
t.Fatalf("err expected %v, got %v", rpctypes.ErrKeyNotFound, err)
}
- _, err = kv.Put(context.TODO(), "foo", "bar")
+ _, err = kv.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
- _, err = kv.Put(context.TODO(), "foo", "", clientv3.WithIgnoreValue())
+ _, err = kv.Put(t.Context(), "foo", "", clientv3.WithIgnoreValue())
require.NoError(t, err)
- rr, rerr := kv.Get(context.TODO(), "foo")
+ rr, rerr := kv.Get(t.Context(), "foo")
require.NoError(t, rerr)
if len(rr.Kvs) != 1 {
t.Fatalf("len(rr.Kvs) expected 1, got %d", len(rr.Kvs))
@@ -139,31 +140,31 @@ func TestKVPutWithIgnoreValue(t *testing.T) {
// TestKVPutWithIgnoreLease ensures that Put with WithIgnoreLease does not affect the existing lease for the key.
func TestKVPutWithIgnoreLease(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.RandClient()
lapi := clus.RandClient()
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- if _, err = kv.Put(context.TODO(), "zoo", "bar", clientv3.WithIgnoreLease()); !errors.Is(err, rpctypes.ErrKeyNotFound) {
+ if _, err = kv.Put(t.Context(), "zoo", "bar", clientv3.WithIgnoreLease()); !errors.Is(err, rpctypes.ErrKeyNotFound) {
t.Fatalf("err expected %v, got %v", rpctypes.ErrKeyNotFound, err)
}
- _, err = kv.Put(context.TODO(), "zoo", "bar", clientv3.WithLease(resp.ID))
+ _, err = kv.Put(t.Context(), "zoo", "bar", clientv3.WithLease(resp.ID))
require.NoError(t, err)
- _, err = kv.Put(context.TODO(), "zoo", "bar1", clientv3.WithIgnoreLease())
+ _, err = kv.Put(t.Context(), "zoo", "bar1", clientv3.WithIgnoreLease())
require.NoError(t, err)
- rr, rerr := kv.Get(context.TODO(), "zoo")
+ rr, rerr := kv.Get(t.Context(), "zoo")
require.NoError(t, rerr)
if len(rr.Kvs) != 1 {
t.Fatalf("len(rr.Kvs) expected 1, got %d", len(rr.Kvs))
@@ -174,9 +175,9 @@ func TestKVPutWithIgnoreLease(t *testing.T) {
}
func TestKVPutWithRequireLeader(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
clus.Members[1].Stop(t)
@@ -190,7 +191,7 @@ func TestKVPutWithRequireLeader(t *testing.T) {
time.Sleep(time.Duration(3*electionTicks) * tickDuration)
kv := clus.Client(0)
- _, err := kv.Put(clientv3.WithRequireLeader(context.Background()), "foo", "bar")
+ _, err := kv.Put(clientv3.WithRequireLeader(t.Context()), "foo", "bar")
if !errors.Is(err, rpctypes.ErrNoLeader) {
t.Fatal(err)
}
@@ -216,13 +217,13 @@ func TestKVPutWithRequireLeader(t *testing.T) {
}
func TestKVRange(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
keySet := []string{"a", "b", "c", "c", "c", "foo", "foo/abc", "fop"}
for i, key := range keySet {
@@ -277,9 +278,9 @@ func TestKVRange(t *testing.T) {
}
func TestKVGetErrConnClosed(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -290,23 +291,23 @@ func TestKVGetErrConnClosed(t *testing.T) {
go func() {
defer close(donec)
- _, err := cli.Get(context.TODO(), "foo")
+ _, err := cli.Get(t.Context(), "foo")
if !clientv3.IsConnCanceled(err) {
t.Errorf("expected %v, got %v", context.Canceled, err)
}
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("kv.Get took too long")
case <-donec:
}
}
func TestKVNewAfterClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -315,27 +316,27 @@ func TestKVNewAfterClose(t *testing.T) {
donec := make(chan struct{})
go func() {
- _, err := cli.Get(context.TODO(), "foo")
+ _, err := cli.Get(t.Context(), "foo")
if !clientv3.IsConnCanceled(err) {
t.Errorf("expected %v, got %v", context.Canceled, err)
}
close(donec)
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("kv.Get took too long")
case <-donec:
}
}
func TestKVDeleteRange(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
tests := []struct {
key string
@@ -378,13 +379,13 @@ func TestKVDeleteRange(t *testing.T) {
}
func TestKVCompactError(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
for i := 0; i < 5; i++ {
if _, err := kv.Put(ctx, "foo", "bar"); err != nil {
@@ -408,13 +409,13 @@ func TestKVCompactError(t *testing.T) {
}
func TestKVCompact(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
for i := 0; i < 10; i++ {
if _, err := kv.Put(ctx, "foo", "bar"); err != nil {
@@ -461,12 +462,87 @@ func TestKVCompact(t *testing.T) {
}
}
+// TestKVGetStreamCompactedError ensures GetStream surfaces the typed
+// rpctypes.ErrCompacted error from the server (matching Get's behavior),
+// rather than the raw gRPC status.
+func TestKVGetStreamCompactedError(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("RangeStream is not supported by the KV client adapter")
+ }
+ integration.BeforeTest(t)
+
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ kv := clus.RandClient()
+ ctx := t.Context()
+
+ for i := 0; i < 5; i++ {
+ _, err := kv.Put(ctx, "foo", "bar")
+ require.NoError(t, err)
+ }
+ _, err := kv.Compact(ctx, 6)
+ require.NoError(t, err)
+
+ _, err = kv.Get(ctx, "foo", clientv3.WithRev(3))
+ require.ErrorIsf(t, err, rpctypes.ErrCompacted, "Get returned %T %v", err, err)
+
+ stream, err := kv.GetStream(ctx, "foo", clientv3.WithRev(3))
+ require.NoError(t, err)
+
+ _, err = clientv3.GetStreamToGetResponse(stream)
+ require.ErrorIsf(t, err, rpctypes.ErrCompacted, "GetStream returned %T %v", err, err)
+}
+
+// TestKVGetKeysOnlyWithCountOnly asserts that when a Range operation
+// with both KeysOnly and CountOnly are specified, CountOnly takes precedence.
+func TestKVGetKeysOnlyWithCountOnly(t *testing.T) {
+ integration.BeforeTest(t)
+
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ kv := clus.RandClient()
+ ctx := t.Context()
+
+ keySet := []string{"a", "b", "c", "c", "c", "foo", "foo/abc", "fop"}
+ for i, key := range keySet {
+ if _, err := kv.Put(ctx, key, ""); err != nil {
+ t.Fatalf("#%d: couldn't put %q (%v)", i, key, err)
+ }
+ }
+ wresp, err := kv.Get(ctx, keySet[0])
+ if err != nil {
+ t.Fatalf("couldn't get key (%v)", err)
+ }
+ wheader := wresp.Header
+
+ opts := []clientv3.OpOption{
+ clientv3.WithFromKey(),
+ clientv3.WithCountOnly(),
+ clientv3.WithKeysOnly(),
+ }
+ resp, err := kv.Get(ctx, "", opts...)
+ if err != nil {
+ t.Fatalf("couldn't execute range with KeysOnly and CountOnly")
+ }
+ if !proto.Equal(wheader, resp.Header) {
+ t.Fatalf("header expected %+v, got %+v", wheader, resp.Header)
+ }
+ if int(resp.Count) != len(slices.Compact(keySet)) {
+ t.Fatalf("response count expected %d, got %d", len(keySet), resp.Count)
+ }
+ if len(resp.Kvs) > 0 {
+ t.Fatalf("response should contain CountOnly")
+ }
+}
+
// TestKVGetRetry ensures get will retry on disconnect.
func TestKVGetRetry(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
clusterSize := 3
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: clusterSize, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: clusterSize, UseBridge: true})
defer clus.Terminate(t)
// because killing leader and following election
@@ -474,7 +550,7 @@ func TestKVGetRetry(t *testing.T) {
fIdx := (clus.WaitLeader(t) + 1) % clusterSize
kv := clus.Client(fIdx)
- ctx := context.TODO()
+ ctx := t.Context()
_, err := kv.Put(ctx, "foo", "bar")
require.NoError(t, err)
@@ -516,15 +592,15 @@ func TestKVGetRetry(t *testing.T) {
// TestKVPutFailGetRetry ensures a get will retry following a failed put.
func TestKVPutFailGetRetry(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
kv := clus.Client(0)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
_, err := kv.Put(ctx, "foo", "bar")
if err == nil {
@@ -534,7 +610,7 @@ func TestKVPutFailGetRetry(t *testing.T) {
donec := make(chan struct{}, 1)
go func() {
// Get will fail, but reconnect will trigger
- gresp, gerr := kv.Get(context.TODO(), "foo")
+ gresp, gerr := kv.Get(t.Context(), "foo")
if gerr != nil {
t.Error(gerr)
}
@@ -556,15 +632,15 @@ func TestKVPutFailGetRetry(t *testing.T) {
// TestKVGetCancel tests that a context cancel on a Get terminates as expected.
func TestKVGetCancel(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
oldconn := clus.Client(0).ActiveConnection()
kv := clus.Client(0)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
resp, err := kv.Get(ctx, "abc")
@@ -579,14 +655,14 @@ func TestKVGetCancel(t *testing.T) {
// TestKVGetStoppedServerAndClose ensures closing after a failed Get works.
func TestKVGetStoppedServerAndClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
// this Get fails and triggers an asynchronous connection retry
_, err := cli.Get(ctx, "abc")
cancel()
@@ -597,15 +673,15 @@ func TestKVGetStoppedServerAndClose(t *testing.T) {
// TestKVPutStoppedServerAndClose ensures closing after a failed Put works.
func TestKVPutStoppedServerAndClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
// get retries on all errors.
// so here we use it to eat the potential broken pipe error for the next put.
// grpc client might see a broken pipe error when we issue the get request before
@@ -616,7 +692,7 @@ func TestKVPutStoppedServerAndClose(t *testing.T) {
t.Fatal(err)
}
- ctx, cancel = context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel = context.WithTimeout(t.Context(), time.Second)
// this Put fails and triggers an asynchronous connection retry
_, err = cli.Put(ctx, "abc", "123")
cancel()
@@ -628,11 +704,11 @@ func TestKVPutStoppedServerAndClose(t *testing.T) {
// TestKVPutAtMostOnce ensures that a Put will only occur at most once
// in the presence of network errors.
func TestKVPutAtMostOnce(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
- _, err := clus.Client(0).Put(context.TODO(), "k", "1")
+ _, err := clus.Client(0).Put(t.Context(), "k", "1")
require.NoError(t, err)
for i := 0; i < 10; i++ {
@@ -645,14 +721,14 @@ func TestKVPutAtMostOnce(t *testing.T) {
time.Sleep(5 * time.Millisecond)
}
}()
- _, err = clus.Client(0).Put(context.TODO(), "k", "v")
+ _, err = clus.Client(0).Put(t.Context(), "k", "v")
<-donec
if err != nil {
break
}
}
- resp, err := clus.Client(0).Get(context.TODO(), "k")
+ resp, err := clus.Client(0).Get(t.Context(), "k")
require.NoError(t, err)
if resp.Kvs[0].Version > 11 {
t.Fatalf("expected version <= 10, got %+v", resp.Kvs[0])
@@ -661,7 +737,7 @@ func TestKVPutAtMostOnce(t *testing.T) {
// TestKVLargeRequests tests various client/server side request limits.
func TestKVLargeRequests(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
tests := []struct {
// make sure that "MaxCallSendMsgSize" < server-side default send/recv limit
maxRequestBytesServer uint
@@ -718,8 +794,8 @@ func TestKVLargeRequests(t *testing.T) {
},
}
for i, test := range tests {
- clus := integration2.NewCluster(t,
- &integration2.ClusterConfig{
+ clus := integration.NewCluster(t,
+ &integration.ClusterConfig{
Size: 1,
MaxRequestBytes: test.maxRequestBytesServer,
ClientMaxCallSendMsgSize: test.maxCallSendBytesClient,
@@ -727,7 +803,7 @@ func TestKVLargeRequests(t *testing.T) {
},
)
cli := clus.Client(0)
- _, err := cli.Put(context.TODO(), "foo", strings.Repeat("a", test.valueSize))
+ _, err := cli.Put(t.Context(), "foo", strings.Repeat("a", test.valueSize))
var etcdErr rpctypes.EtcdError
if errors.As(err, &etcdErr) {
@@ -740,7 +816,7 @@ func TestKVLargeRequests(t *testing.T) {
// put request went through, now expects large response back
if err == nil {
- _, err = cli.Get(context.TODO(), "foo")
+ _, err = cli.Get(t.Context(), "foo")
if err != nil {
t.Errorf("#%d: get expected no error, got %v", i, err)
}
@@ -752,9 +828,9 @@ func TestKVLargeRequests(t *testing.T) {
// TestKVForLearner ensures learner member only accepts serializable read request.
func TestKVForLearner(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
// we have to add and launch learner member after initial cluster was created, because
@@ -780,10 +856,9 @@ func TestKVForLearner(t *testing.T) {
cfg := clientv3.Config{
Endpoints: []string{learnerEp},
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
// this client only has endpoint of the learner member
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
if err != nil {
t.Fatalf("failed to create clientv3: %v", err)
}
@@ -819,7 +894,7 @@ func TestKVForLearner(t *testing.T) {
}
for idx, test := range tests {
- _, err := cli.Do(context.TODO(), test.op)
+ _, err := cli.Do(t.Context(), test.op)
if err != nil && !test.wErr {
t.Errorf("%d: expect no error, got %v", idx, err)
}
@@ -831,9 +906,9 @@ func TestKVForLearner(t *testing.T) {
// TestBalancerSupportLearner verifies that balancer's retry and failover mechanism supports cluster with learner member
func TestBalancerSupportLearner(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, DisableStrictReconfigCheck: true})
defer clus.Terminate(t)
// we have to add and launch learner member after initial cluster was created, because
@@ -853,9 +928,8 @@ func TestBalancerSupportLearner(t *testing.T) {
cfg := clientv3.Config{
Endpoints: []string{learnerEp},
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
if err != nil {
t.Fatalf("failed to create clientv3: %v", err)
}
@@ -864,14 +938,14 @@ func TestBalancerSupportLearner(t *testing.T) {
// wait until learner member is ready
<-clus.Members[3].ReadyNotify()
- if _, err = cli.Get(context.Background(), "foo"); err == nil {
+ if _, err = cli.Get(t.Context(), "foo"); err == nil {
t.Fatalf("expect Get request to learner to fail, got no error")
}
t.Logf("Expected: Read from learner error: %v", err)
eps := []string{learnerEp, clus.Members[0].GRPCURL}
cli.SetEndpoints(eps...)
- if _, err := cli.Get(context.Background(), "foo"); err != nil {
+ if _, err := cli.Get(t.Context(), "foo"); err != nil {
t.Errorf("expect no error (balancer should retry when request to learner fails), got error: %v", err)
}
}
diff --git a/tests/integration/clientv3/lease/lease_test.go b/tests/integration/clientv3/lease/lease_test.go
index eaf4a75ff591..074c5566eb0e 100644
--- a/tests/integration/clientv3/lease/lease_test.go
+++ b/tests/integration/clientv3/lease/lease_test.go
@@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
"sort"
+ "strconv"
"sync"
"testing"
"time"
@@ -29,109 +30,101 @@ import (
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestLeaseNotFoundError(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.RandClient()
- _, err := kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(500)))
- if !errors.Is(err, rpctypes.ErrLeaseNotFound) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrLeaseNotFound, err)
- }
+ _, err := kv.Put(t.Context(), "foo", "bar", clientv3.WithLease(clientv3.LeaseID(500)))
+ require.ErrorIsf(t, err, rpctypes.ErrLeaseNotFound, "expected %v, got %v", rpctypes.ErrLeaseNotFound, err)
}
func TestLeaseGrant(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lapi := clus.RandClient()
kv := clus.RandClient()
- _, merr := lapi.Grant(context.Background(), clientv3.MaxLeaseTTL+1)
- if !errors.Is(merr, rpctypes.ErrLeaseTTLTooLarge) {
- t.Fatalf("err = %v, want %v", merr, rpctypes.ErrLeaseTTLTooLarge)
- }
+ _, merr := lapi.Grant(t.Context(), clientv3.MaxLeaseTTL+1)
+ require.ErrorIsf(t, merr, rpctypes.ErrLeaseTTLTooLarge, "err = %v, want %v", merr, rpctypes.ErrLeaseTTLTooLarge)
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- _, err = kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID))
- if err != nil {
- t.Fatalf("failed to create key with lease %v", err)
- }
+ _, err = kv.Put(t.Context(), "foo", "bar", clientv3.WithLease(resp.ID))
+ require.NoErrorf(t, err, "failed to create key with lease %v", err)
}
func TestLeaseRevoke(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lapi := clus.RandClient()
kv := clus.RandClient()
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- _, err = lapi.Revoke(context.Background(), resp.ID)
+ _, err = lapi.Revoke(t.Context(), resp.ID)
if err != nil {
t.Errorf("failed to revoke lease %v", err)
}
- _, err = kv.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID))
- if !errors.Is(err, rpctypes.ErrLeaseNotFound) {
- t.Fatalf("err = %v, want %v", err, rpctypes.ErrLeaseNotFound)
- }
+ _, err = kv.Put(t.Context(), "foo", "bar", clientv3.WithLease(resp.ID))
+ require.ErrorIsf(t, err, rpctypes.ErrLeaseNotFound, "err = %v, want %v", err, rpctypes.ErrLeaseNotFound)
}
func TestLeaseKeepAliveOnce(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lapi := clus.RandClient()
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- _, err = lapi.KeepAliveOnce(context.Background(), resp.ID)
+ _, err = lapi.KeepAliveOnce(t.Context(), resp.ID)
if err != nil {
t.Errorf("failed to keepalive lease %v", err)
}
- _, err = lapi.KeepAliveOnce(context.Background(), clientv3.LeaseID(0))
+ _, err = lapi.KeepAliveOnce(t.Context(), clientv3.LeaseID(0))
if !errors.Is(err, rpctypes.ErrLeaseNotFound) {
t.Errorf("expected %v, got %v", rpctypes.ErrLeaseNotFound, err)
}
}
func TestLeaseKeepAlive(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lapi := clus.Client(0)
clus.TakeClient(0)
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
@@ -141,7 +134,7 @@ func TestLeaseKeepAlive(t *testing.T) {
_ func()
}
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
rc, kerr := lapi.KeepAlive(uncomparableCtx{Context: ctx}, resp.ID)
if kerr != nil {
@@ -153,15 +146,13 @@ func TestLeaseKeepAlive(t *testing.T) {
t.Errorf("chan is closed, want not closed")
}
- if kresp == nil {
- t.Fatalf("unexpected null response")
- }
+ require.NotNilf(t, kresp, "unexpected null response")
if kresp.ID != resp.ID {
t.Errorf("ID = %x, want %x", kresp.ID, resp.ID)
}
- ctx2, cancel2 := context.WithCancel(context.Background())
+ ctx2, cancel2 := context.WithCancel(t.Context())
rc2, kerr2 := lapi.KeepAlive(uncomparableCtx{Context: ctx2}, resp.ID)
if kerr2 != nil {
t.Errorf("failed to keepalive lease %v", kerr2)
@@ -190,18 +181,18 @@ func TestLeaseKeepAlive(t *testing.T) {
}
func TestLeaseKeepAliveSeconds(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
- resp, err := cli.Grant(context.Background(), 3)
+ resp, err := cli.Grant(t.Context(), 3)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- rc, kerr := cli.KeepAlive(context.Background(), resp.ID)
+ rc, kerr := cli.KeepAlive(t.Context(), resp.ID)
if kerr != nil {
t.Errorf("failed to keepalive lease %v", kerr)
}
@@ -219,20 +210,20 @@ func TestLeaseKeepAliveSeconds(t *testing.T) {
func TestLeaseKeepAliveHandleFailure(t *testing.T) {
t.Skip("test it when we have a cluster client")
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
// TODO: change this line to get a cluster client
lapi := clus.RandClient()
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- rc, kerr := lapi.KeepAlive(context.Background(), resp.ID)
+ rc, kerr := lapi.KeepAlive(t.Context(), resp.ID)
if kerr != nil {
t.Errorf("failed to keepalive lease %v", kerr)
}
@@ -274,22 +265,22 @@ type leaseCh struct {
// TestLeaseKeepAliveNotFound ensures a revoked lease won't halt other leases.
func TestLeaseKeepAliveNotFound(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.RandClient()
var lchs []leaseCh
for i := 0; i < 3; i++ {
- resp, rerr := cli.Grant(context.TODO(), 5)
+ resp, rerr := cli.Grant(t.Context(), 5)
require.NoError(t, rerr)
- kach, kaerr := cli.KeepAlive(context.Background(), resp.ID)
+ kach, kaerr := cli.KeepAlive(t.Context(), resp.ID)
require.NoError(t, kaerr)
lchs = append(lchs, leaseCh{resp.ID, kach})
}
- _, err := cli.Revoke(context.TODO(), lchs[1].lid)
+ _, err := cli.Revoke(t.Context(), lchs[1].lid)
require.NoError(t, err)
<-lchs[0].ch
@@ -302,9 +293,9 @@ func TestLeaseKeepAliveNotFound(t *testing.T) {
}
func TestLeaseGrantErrConnClosed(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -314,7 +305,7 @@ func TestLeaseGrantErrConnClosed(t *testing.T) {
donec := make(chan struct{})
go func() {
defer close(donec)
- _, err := cli.Grant(context.TODO(), 5)
+ _, err := cli.Grant(t.Context(), 5)
if !clientv3.IsConnCanceled(err) {
// context.Canceled if grpc-go balancer calls 'Get' with an inflight client.Close.
t.Errorf("expected %v, or server unavailable, got %v", context.Canceled, err)
@@ -322,7 +313,7 @@ func TestLeaseGrantErrConnClosed(t *testing.T) {
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("le.Grant took too long")
case <-donec:
}
@@ -332,18 +323,16 @@ func TestLeaseGrantErrConnClosed(t *testing.T) {
// queue is full thus dropping keepalive response sends,
// keepalive request is sent with the same rate of TTL / 3.
func TestLeaseKeepAliveFullResponseQueue(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lapi := clus.Client(0)
// expect lease keepalive every 10-second
- lresp, err := lapi.Grant(context.Background(), 30)
- if err != nil {
- t.Fatalf("failed to create lease %v", err)
- }
+ lresp, err := lapi.Grant(t.Context(), 30)
+ require.NoErrorf(t, err, "failed to create lease %v", err)
id := lresp.ID
old := clientv3.LeaseResponseChSize
@@ -353,28 +342,24 @@ func TestLeaseKeepAliveFullResponseQueue(t *testing.T) {
clientv3.LeaseResponseChSize = 0
// never fetch from response queue, and let it become full
- _, err = lapi.KeepAlive(context.Background(), id)
- if err != nil {
- t.Fatalf("failed to keepalive lease %v", err)
- }
+ _, err = lapi.KeepAlive(t.Context(), id)
+ require.NoErrorf(t, err, "failed to keepalive lease %v", err)
// TTL should not be refreshed after 3 seconds
// expect keepalive to be triggered after TTL/3
time.Sleep(3 * time.Second)
- tr, terr := lapi.TimeToLive(context.Background(), id)
- if terr != nil {
- t.Fatalf("failed to get lease information %v", terr)
- }
+ tr, terr := lapi.TimeToLive(t.Context(), id)
+ require.NoErrorf(t, terr, "failed to get lease information %v", terr)
if tr.TTL >= 29 {
t.Errorf("unexpected kept-alive lease TTL %d", tr.TTL)
}
}
func TestLeaseGrantNewAfterClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -383,27 +368,27 @@ func TestLeaseGrantNewAfterClose(t *testing.T) {
donec := make(chan struct{})
go func() {
- _, err := cli.Grant(context.TODO(), 5)
+ _, err := cli.Grant(t.Context(), 5)
if !clientv3.IsConnCanceled(err) {
t.Errorf("expected %v or server unavailable, got %v", context.Canceled, err)
}
close(donec)
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("le.Grant took too long")
case <-donec:
}
}
func TestLeaseRevokeNewAfterClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
- resp, err := cli.Grant(context.TODO(), 5)
+ resp, err := cli.Grant(t.Context(), 5)
require.NoError(t, err)
leaseID := resp.ID
@@ -412,7 +397,7 @@ func TestLeaseRevokeNewAfterClose(t *testing.T) {
errMsgCh := make(chan string, 1)
go func() {
- _, err := cli.Revoke(context.TODO(), leaseID)
+ _, err := cli.Revoke(t.Context(), leaseID)
if !clientv3.IsConnCanceled(err) {
errMsgCh <- fmt.Sprintf("expected %v or server unavailable, got %v", context.Canceled, err)
} else {
@@ -420,41 +405,37 @@ func TestLeaseRevokeNewAfterClose(t *testing.T) {
}
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("le.Revoke took too long")
case errMsg := <-errMsgCh:
- if errMsg != "" {
- t.Fatalf("%v", errMsg)
- }
+ require.Empty(t, errMsg)
}
}
// TestLeaseKeepAliveCloseAfterDisconnectRevoke ensures the keep alive channel is closed
// following a disconnection, lease revoke, then reconnect.
func TestLeaseKeepAliveCloseAfterDisconnectRevoke(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
// setup lease and do a keepalive
- resp, err := cli.Grant(context.Background(), 10)
+ resp, err := cli.Grant(t.Context(), 10)
require.NoError(t, err)
- rc, kerr := cli.KeepAlive(context.Background(), resp.ID)
+ rc, kerr := cli.KeepAlive(t.Context(), resp.ID)
require.NoError(t, kerr)
kresp := <-rc
- if kresp.ID != resp.ID {
- t.Fatalf("ID = %x, want %x", kresp.ID, resp.ID)
- }
+ require.Equalf(t, kresp.ID, resp.ID, "ID = %x, want %x", kresp.ID, resp.ID)
// keep client disconnected
clus.Members[0].Stop(t)
time.Sleep(time.Second)
clus.WaitLeader(t)
- _, err = clus.Client(1).Revoke(context.TODO(), resp.ID)
+ _, err = clus.Client(1).Revoke(t.Context(), resp.ID)
require.NoError(t, err)
clus.Members[0].Restart(t)
@@ -473,25 +454,23 @@ func TestLeaseKeepAliveCloseAfterDisconnectRevoke(t *testing.T) {
// TestLeaseKeepAliveInitTimeout ensures the keep alive channel closes if
// the initial keep alive request never gets a response.
func TestLeaseKeepAliveInitTimeout(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
// setup lease and do a keepalive
- resp, err := cli.Grant(context.Background(), 5)
+ resp, err := cli.Grant(t.Context(), 5)
require.NoError(t, err)
// keep client disconnected
clus.Members[0].Stop(t)
- rc, kerr := cli.KeepAlive(context.Background(), resp.ID)
+ rc, kerr := cli.KeepAlive(t.Context(), resp.ID)
require.NoError(t, kerr)
select {
case ka, ok := <-rc:
- if ok {
- t.Fatalf("unexpected keepalive %v, expected closed channel", ka)
- }
+ require.Falsef(t, ok, "unexpected keepalive %v, expected closed channel", ka)
case <-time.After(10 * time.Second):
t.Fatalf("keepalive channel did not close")
}
@@ -502,29 +481,26 @@ func TestLeaseKeepAliveInitTimeout(t *testing.T) {
// TestLeaseKeepAliveTTLTimeout ensures the keep alive channel closes if
// a keep alive request after the first never gets a response.
func TestLeaseKeepAliveTTLTimeout(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
// setup lease and do a keepalive
- resp, err := cli.Grant(context.Background(), 5)
+ resp, err := cli.Grant(t.Context(), 5)
require.NoError(t, err)
- rc, kerr := cli.KeepAlive(context.Background(), resp.ID)
+ rc, kerr := cli.KeepAlive(t.Context(), resp.ID)
require.NoError(t, kerr)
- if kresp := <-rc; kresp.ID != resp.ID {
- t.Fatalf("ID = %x, want %x", kresp.ID, resp.ID)
- }
+ kresp := <-rc
+ require.Equalf(t, kresp.ID, resp.ID, "ID = %x, want %x", kresp.ID, resp.ID)
// keep client disconnected
clus.Members[0].Stop(t)
select {
case ka, ok := <-rc:
- if ok {
- t.Fatalf("unexpected keepalive %v, expected closed channel", ka)
- }
+ require.Falsef(t, ok, "unexpected keepalive %v, expected closed channel", ka)
case <-time.After(10 * time.Second):
t.Fatalf("keepalive channel did not close")
}
@@ -533,15 +509,15 @@ func TestLeaseKeepAliveTTLTimeout(t *testing.T) {
}
func TestLeaseTimeToLive(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
c := clus.RandClient()
lapi := c
- resp, err := lapi.Grant(context.Background(), 10)
+ resp, err := lapi.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
@@ -549,22 +525,18 @@ func TestLeaseTimeToLive(t *testing.T) {
kv := clus.RandClient()
keys := []string{"foo1", "foo2"}
for i := range keys {
- _, err = kv.Put(context.TODO(), keys[i], "bar", clientv3.WithLease(resp.ID))
+ _, err = kv.Put(t.Context(), keys[i], "bar", clientv3.WithLease(resp.ID))
require.NoError(t, err)
}
// linearized read to ensure Puts propagated to server backing lapi
- _, err = c.Get(context.TODO(), "abc")
+ _, err = c.Get(t.Context(), "abc")
require.NoError(t, err)
- lresp, lerr := lapi.TimeToLive(context.Background(), resp.ID, clientv3.WithAttachedKeys())
+ lresp, lerr := lapi.TimeToLive(t.Context(), resp.ID, clientv3.WithAttachedKeys())
require.NoError(t, lerr)
- if lresp.ID != resp.ID {
- t.Fatalf("leaseID expected %d, got %d", resp.ID, lresp.ID)
- }
- if lresp.GrantedTTL != int64(10) {
- t.Fatalf("GrantedTTL expected %d, got %d", 10, lresp.GrantedTTL)
- }
+ require.Equalf(t, lresp.ID, resp.ID, "leaseID expected %d, got %d", resp.ID, lresp.ID)
+ require.Equalf(t, int64(10), lresp.GrantedTTL, "GrantedTTL expected %d, got %d", 10, lresp.GrantedTTL)
if lresp.TTL == 0 || lresp.TTL > lresp.GrantedTTL {
t.Fatalf("unexpected TTL %d (granted %d)", lresp.TTL, lresp.GrantedTTL)
}
@@ -573,94 +545,76 @@ func TestLeaseTimeToLive(t *testing.T) {
ks[i] = string(lresp.Keys[i])
}
sort.Strings(ks)
- if !reflect.DeepEqual(ks, keys) {
- t.Fatalf("keys expected %v, got %v", keys, ks)
- }
+ require.Truef(t, reflect.DeepEqual(ks, keys), "keys expected %v, got %v", keys, ks)
- lresp, lerr = lapi.TimeToLive(context.Background(), resp.ID)
+ lresp, lerr = lapi.TimeToLive(t.Context(), resp.ID)
require.NoError(t, lerr)
- if len(lresp.Keys) != 0 {
- t.Fatalf("unexpected keys %+v", lresp.Keys)
- }
+ require.Emptyf(t, lresp.Keys, "unexpected keys %+v", lresp.Keys)
}
func TestLeaseTimeToLiveLeaseNotFound(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.RandClient()
- resp, err := cli.Grant(context.Background(), 10)
+ resp, err := cli.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- _, err = cli.Revoke(context.Background(), resp.ID)
+ _, err = cli.Revoke(t.Context(), resp.ID)
if err != nil {
t.Errorf("failed to Revoke lease %v", err)
}
- lresp, err := cli.TimeToLive(context.Background(), resp.ID)
+ lresp, err := cli.TimeToLive(t.Context(), resp.ID)
// TimeToLive() should return a response with TTL=-1.
- if err != nil {
- t.Fatalf("expected err to be nil")
- }
- if lresp == nil {
- t.Fatalf("expected lresp not to be nil")
- }
- if lresp.ResponseHeader == nil {
- t.Fatalf("expected ResponseHeader not to be nil")
- }
- if lresp.ID != resp.ID {
- t.Fatalf("expected Lease ID %v, but got %v", resp.ID, lresp.ID)
- }
- if lresp.TTL != -1 {
- t.Fatalf("expected TTL %v, but got %v", lresp.TTL, lresp.TTL)
- }
+ require.NoErrorf(t, err, "expected err to be nil")
+ require.NotNilf(t, lresp, "expected lresp not to be nil")
+ require.NotNilf(t, lresp.ResponseHeader, "expected ResponseHeader not to be nil")
+ require.Equalf(t, lresp.ID, resp.ID, "expected Lease ID %v, but got %v", resp.ID, lresp.ID)
+ require.Equalf(t, int64(-1), lresp.TTL, "expected TTL %v, but got %v", int64(-1), lresp.TTL)
}
func TestLeaseLeases(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.RandClient()
var ids []clientv3.LeaseID
for i := 0; i < 5; i++ {
- resp, err := cli.Grant(context.Background(), 10)
+ resp, err := cli.Grant(t.Context(), 10)
if err != nil {
t.Errorf("failed to create lease %v", err)
}
ids = append(ids, resp.ID)
}
- resp, err := cli.Leases(context.Background())
+ resp, err := cli.Leases(t.Context())
require.NoError(t, err)
- if len(resp.Leases) != 5 {
- t.Fatalf("len(resp.Leases) expected 5, got %d", len(resp.Leases))
- }
+ require.Lenf(t, resp.Leases, 5, "len(resp.Leases) expected 5, got %d", len(resp.Leases))
for i := range resp.Leases {
- if ids[i] != resp.Leases[i].ID {
- t.Fatalf("#%d: lease ID expected %d, got %d", i, ids[i], resp.Leases[i].ID)
- }
+ require.Equalf(t, ids[i], resp.Leases[i].ID, "#%d: lease ID expected %d, got %d", i, ids[i], resp.Leases[i].ID)
}
}
// TestLeaseRenewLostQuorum ensures keepalives work after losing quorum
// for a while.
func TestLeaseRenewLostQuorum(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
- r, err := cli.Grant(context.TODO(), 4)
+ r, err := cli.Grant(t.Context(), 4)
require.NoError(t, err)
- kctx, kcancel := context.WithCancel(context.Background())
+ kctx, kcancel := context.WithCancel(t.Context())
defer kcancel()
ka, err := cli.KeepAlive(kctx, r.ID)
require.NoError(t, err)
@@ -686,21 +640,19 @@ func TestLeaseRenewLostQuorum(t *testing.T) {
select {
case _, ok := <-ka:
- if !ok {
- t.Fatalf("keepalive closed")
- }
+ require.Truef(t, ok, "keepalive closed")
case <-time.After(time.Duration(r.TTL) * time.Second):
t.Fatalf("timed out waiting for keepalive")
}
}
func TestLeaseKeepAliveLoopExit(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx := context.Background()
+ ctx := t.Context()
cli := clus.Client(0)
clus.TakeClient(0)
@@ -710,17 +662,15 @@ func TestLeaseKeepAliveLoopExit(t *testing.T) {
_, err = cli.KeepAlive(ctx, resp.ID)
var keepAliveHaltedErr clientv3.ErrKeepAliveHalted
- if !errors.As(err, &keepAliveHaltedErr) {
- t.Fatalf("expected %T, got %v(%T)", clientv3.ErrKeepAliveHalted{}, err, err)
- }
+ require.ErrorAsf(t, err, &keepAliveHaltedErr, "expected %T, got %v(%T)", clientv3.ErrKeepAliveHalted{}, err, err)
}
// TestV3LeaseFailureOverlap issues Grant and KeepAlive requests to a cluster
// before, during, and after quorum loss to confirm Grant/KeepAlive tolerates
// transient cluster failure.
func TestV3LeaseFailureOverlap(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2, UseBridge: true})
defer clus.Terminate(t)
numReqs := 5
@@ -772,21 +722,21 @@ func TestV3LeaseFailureOverlap(t *testing.T) {
// TestLeaseWithRequireLeader checks keep-alive channel close when no leader.
func TestLeaseWithRequireLeader(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2, UseBridge: true})
defer clus.Terminate(t)
c := clus.Client(0)
- lid1, err1 := c.Grant(context.TODO(), 60)
+ lid1, err1 := c.Grant(t.Context(), 60)
require.NoError(t, err1)
- lid2, err2 := c.Grant(context.TODO(), 60)
+ lid2, err2 := c.Grant(t.Context(), 60)
require.NoError(t, err2)
// kaReqLeader close if the leader is lost
- kaReqLeader, kerr1 := c.KeepAlive(clientv3.WithRequireLeader(context.TODO()), lid1.ID)
+ kaReqLeader, kerr1 := c.KeepAlive(clientv3.WithRequireLeader(t.Context()), lid1.ID)
require.NoError(t, kerr1)
// kaWait will wait even if the leader is lost
- kaWait, kerr2 := c.KeepAlive(context.TODO(), lid2.ID)
+ kaWait, kerr2 := c.KeepAlive(t.Context(), lid2.ID)
require.NoError(t, kerr2)
select {
@@ -800,6 +750,9 @@ func TestLeaseWithRequireLeader(t *testing.T) {
t.Fatalf("leader not required first keep-alive timed out")
}
+ prevUnavailableCount := getLeaseKeepAliveMetric(t, clus.Members[0], "Unavailable")
+ prevCanceledCount := getLeaseKeepAliveMetric(t, clus.Members[0], "Canceled")
+
clus.Members[1].Stop(t)
// kaReqLeader may issue multiple requests while waiting for the first
// response from proxy server; drain any stray keepalive responses
@@ -813,18 +766,37 @@ func TestLeaseWithRequireLeader(t *testing.T) {
select {
case resp, ok := <-kaReqLeader:
- if ok {
- t.Fatalf("expected closed require leader, got response %+v", resp)
- }
+ require.Falsef(t, ok, "expected closed require leader, got response %+v", resp)
case <-time.After(5 * time.Second):
t.Fatal("keepalive with require leader took too long to close")
}
+
+ require.Eventuallyf(t, func() bool {
+ return getLeaseKeepAliveMetric(t, clus.Members[0], "Unavailable") > prevUnavailableCount
+ }, 3*time.Second, 100*time.Millisecond,
+ "expected Unavailable metric to increase after leader loss, prev count: %d", prevUnavailableCount)
+ // Ensure the error is Unavailable, not Canceled
+ currentCanceledCount := getLeaseKeepAliveMetric(t, clus.Members[0], "Canceled")
+ require.Equalf(t, prevCanceledCount, currentCanceledCount,
+ "Canceled metric should not change, expected %d, got %d", prevCanceledCount, currentCanceledCount)
+
select {
case _, ok := <-kaWait:
- if !ok {
- t.Fatalf("got closed channel with no require leader, expected non-closed")
- }
+ require.Truef(t, ok, "got closed channel with no require leader, expected non-closed")
case <-time.After(10 * time.Millisecond):
// wait some to detect any closes happening soon after kaReqLeader closing
}
}
+
+func getLeaseKeepAliveMetric(t *testing.T, member *integration.Member, grpcCode string) int64 {
+ t.Helper()
+ metricVal, err := member.Metric(
+ "grpc_server_handled_total",
+ `grpc_method="LeaseKeepAlive"`,
+ fmt.Sprintf(`grpc_code="%v"`, grpcCode),
+ )
+ require.NoError(t, err)
+ count, err := strconv.ParseInt(metricVal, 10, 64)
+ require.NoError(t, err)
+ return count
+}
diff --git a/tests/integration/clientv3/lease/leasing_test.go b/tests/integration/clientv3/lease/leasing_test.go
index 9d6d77b353f5..e6f86166ff7e 100644
--- a/tests/integration/clientv3/lease/leasing_test.go
+++ b/tests/integration/clientv3/lease/leasing_test.go
@@ -24,19 +24,22 @@ import (
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "google.golang.org/protobuf/testing/protocmp"
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
"go.etcd.io/etcd/client/v3/leasing"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestLeasingPutGet(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
lKV1, closeLKV1, err := leasing.NewKV(clus.Client(0), "foo/")
@@ -51,43 +54,43 @@ func TestLeasingPutGet(t *testing.T) {
require.NoError(t, err)
defer closeLKV3()
- resp, err := lKV1.Get(context.TODO(), "abc")
+ resp, err := lKV1.Get(t.Context(), "abc")
require.NoError(t, err)
if len(resp.Kvs) != 0 {
t.Errorf("expected nil, got %q", resp.Kvs[0].Key)
}
- _, err = lKV1.Put(context.TODO(), "abc", "def")
+ _, err = lKV1.Put(t.Context(), "abc", "def")
require.NoError(t, err)
- resp, err = lKV2.Get(context.TODO(), "abc")
+ resp, err = lKV2.Get(t.Context(), "abc")
require.NoError(t, err)
if string(resp.Kvs[0].Key) != "abc" {
t.Errorf("expected key=%q, got key=%q", "abc", resp.Kvs[0].Key)
}
if string(resp.Kvs[0].Value) != "def" {
- t.Errorf("expected value=%q, got value=%q", "bar", resp.Kvs[0].Value)
+ t.Errorf("expected value=%q, got value=%q", "def", resp.Kvs[0].Value)
}
- _, err = lKV3.Get(context.TODO(), "abc")
+ _, err = lKV3.Get(t.Context(), "abc")
require.NoError(t, err)
- _, err = lKV2.Put(context.TODO(), "abc", "ghi")
+ _, err = lKV2.Put(t.Context(), "abc", "ghi")
require.NoError(t, err)
- resp, err = lKV3.Get(context.TODO(), "abc")
+ resp, err = lKV3.Get(t.Context(), "abc")
require.NoError(t, err)
if string(resp.Kvs[0].Key) != "abc" {
t.Errorf("expected key=%q, got key=%q", "abc", resp.Kvs[0].Key)
}
if string(resp.Kvs[0].Value) != "ghi" {
- t.Errorf("expected value=%q, got value=%q", "bar", resp.Kvs[0].Value)
+ t.Errorf("expected value=%q, got value=%q", "ghi", resp.Kvs[0].Value)
}
}
// TestLeasingInterval checks the leasing KV fetches key intervals.
func TestLeasingInterval(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -96,102 +99,94 @@ func TestLeasingInterval(t *testing.T) {
keys := []string{"abc/a", "abc/b", "abc/a/a"}
for _, k := range keys {
- _, err = clus.Client(0).Put(context.TODO(), k, "v")
+ _, err = clus.Client(0).Put(t.Context(), k, "v")
require.NoError(t, err)
}
- resp, err := lkv.Get(context.TODO(), "abc/", clientv3.WithPrefix())
+ resp, err := lkv.Get(t.Context(), "abc/", clientv3.WithPrefix())
require.NoError(t, err)
- if len(resp.Kvs) != 3 {
- t.Fatalf("expected keys %+v, got response keys %+v", keys, resp.Kvs)
- }
+ require.Lenf(t, resp.Kvs, 3, "expected keys %+v, got response keys %+v", keys, resp.Kvs)
// load into cache
- _, err = lkv.Get(context.TODO(), "abc/a")
+ _, err = lkv.Get(t.Context(), "abc/a")
require.NoError(t, err)
// get when prefix is also a cached key
- resp, err = lkv.Get(context.TODO(), "abc/a", clientv3.WithPrefix())
+ resp, err = lkv.Get(t.Context(), "abc/a", clientv3.WithPrefix())
require.NoError(t, err)
- if len(resp.Kvs) != 2 {
- t.Fatalf("expected keys %+v, got response keys %+v", keys, resp.Kvs)
- }
+ require.Lenf(t, resp.Kvs, 2, "expected keys %+v, got response keys %+v", keys, resp.Kvs)
}
// TestLeasingPutInvalidateNew checks the leasing KV updates its cache on a Put to a new key.
func TestLeasingPutInvalidateNew(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
- _, err = lkv.Put(context.TODO(), "k", "v")
+ _, err = lkv.Put(t.Context(), "k", "v")
require.NoError(t, err)
- lkvResp, err := lkv.Get(context.TODO(), "k")
+ lkvResp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
- cResp, cerr := clus.Client(0).Get(context.TODO(), "k")
+ cResp, cerr := clus.Client(0).Get(t.Context(), "k")
require.NoError(t, cerr)
- if !reflect.DeepEqual(lkvResp, cResp) {
- t.Fatalf(`expected %+v, got response %+v`, cResp, lkvResp)
- }
+ require.Emptyf(t, cmp.Diff((*pb.RangeResponse)(lkvResp), (*pb.RangeResponse)(cResp), protocmp.Transform()), "-want, +got")
}
// TestLeasingPutInvalidateExisting checks the leasing KV updates its cache on a Put to an existing key.
func TestLeasingPutInvalidateExisting(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- _, err := clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err := clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
- _, err = lkv.Put(context.TODO(), "k", "v")
+ _, err = lkv.Put(t.Context(), "k", "v")
require.NoError(t, err)
- lkvResp, err := lkv.Get(context.TODO(), "k")
+ lkvResp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
- cResp, cerr := clus.Client(0).Get(context.TODO(), "k")
+ cResp, cerr := clus.Client(0).Get(t.Context(), "k")
require.NoError(t, cerr)
- if !reflect.DeepEqual(lkvResp, cResp) {
- t.Fatalf(`expected %+v, got response %+v`, cResp, lkvResp)
- }
+ require.Emptyf(t, cmp.Diff((*pb.RangeResponse)(lkvResp), (*pb.RangeResponse)(cResp), protocmp.Transform()), "-want, +got")
}
// TestLeasingGetNoLeaseTTL checks a key with a TTL is not leased.
func TestLeasingGetNoLeaseTTL(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- lresp, err := clus.Client(0).Grant(context.TODO(), 60)
+ lresp, err := clus.Client(0).Grant(t.Context(), 60)
require.NoError(t, err)
- _, err = clus.Client(0).Put(context.TODO(), "k", "v", clientv3.WithLease(lresp.ID))
+ _, err = clus.Client(0).Put(t.Context(), "k", "v", clientv3.WithLease(lresp.ID))
require.NoError(t, err)
- gresp, err := lkv.Get(context.TODO(), "k")
+ gresp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
assert.Len(t, gresp.Kvs, 1)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
_, err = lkv.Get(ctx, "k")
cancel()
assert.Equal(t, err, ctx.Err())
@@ -200,32 +195,30 @@ func TestLeasingGetNoLeaseTTL(t *testing.T) {
// TestLeasingGetSerializable checks the leasing KV can make serialized requests
// when the etcd cluster is partitioned.
func TestLeasingGetSerializable(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "cached", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "cached", "abc")
require.NoError(t, err)
- _, err = lkv.Get(context.TODO(), "cached")
+ _, err = lkv.Get(t.Context(), "cached")
require.NoError(t, err)
clus.Members[1].Stop(t)
// don't necessarily try to acquire leasing key ownership for new key
- resp, err := lkv.Get(context.TODO(), "uncached", clientv3.WithSerializable())
+ resp, err := lkv.Get(t.Context(), "uncached", clientv3.WithSerializable())
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf(`expected no keys, got response %+v`, resp)
- }
+ require.Emptyf(t, resp.Kvs, `expected no keys, got response %+v`, resp)
clus.Members[0].Stop(t)
// leasing key ownership should have "cached" locally served
- cachedResp, err := lkv.Get(context.TODO(), "cached", clientv3.WithSerializable())
+ cachedResp, err := lkv.Get(t.Context(), "cached", clientv3.WithSerializable())
require.NoError(t, err)
if len(cachedResp.Kvs) != 1 || string(cachedResp.Kvs[0].Value) != "abc" {
t.Fatalf(`expected "cached"->"abc", got response %+v`, cachedResp)
@@ -234,20 +227,20 @@ func TestLeasingGetSerializable(t *testing.T) {
// TestLeasingPrevKey checks the cache respects WithPrevKV on puts.
func TestLeasingPrevKey(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
// acquire leasing key
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
- resp, err := lkv.Put(context.TODO(), "k", "def", clientv3.WithPrevKV())
+ resp, err := lkv.Put(t.Context(), "k", "def", clientv3.WithPrevKV())
require.NoError(t, err)
if resp.PrevKv == nil || string(resp.PrevKv.Value) != "abc" {
t.Fatalf(`expected PrevKV.Value="abc", got response %+v`, resp)
@@ -256,27 +249,27 @@ func TestLeasingPrevKey(t *testing.T) {
// TestLeasingRevGet checks the cache respects Get by Revision.
func TestLeasingRevGet(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- putResp, err := clus.Client(0).Put(context.TODO(), "k", "abc")
+ putResp, err := clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
- _, err = clus.Client(0).Put(context.TODO(), "k", "def")
+ _, err = clus.Client(0).Put(t.Context(), "k", "def")
require.NoError(t, err)
// check historic revision
- getResp, gerr := lkv.Get(context.TODO(), "k", clientv3.WithRev(putResp.Header.Revision))
+ getResp, gerr := lkv.Get(t.Context(), "k", clientv3.WithRev(putResp.Header.Revision))
require.NoError(t, gerr)
if len(getResp.Kvs) != 1 || string(getResp.Kvs[0].Value) != "abc" {
t.Fatalf(`expected "k"->"abc" at rev=%d, got response %+v`, putResp.Header.Revision, getResp)
}
// check current revision
- getResp, gerr = lkv.Get(context.TODO(), "k")
+ getResp, gerr = lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
if len(getResp.Kvs) != 1 || string(getResp.Kvs[0].Value) != "def" {
t.Fatalf(`expected "k"->"def" at rev=%d, got response %+v`, putResp.Header.Revision, getResp)
@@ -285,18 +278,18 @@ func TestLeasingRevGet(t *testing.T) {
// TestLeasingGetWithOpts checks options that can be served through the cache do not depend on the server.
func TestLeasingGetWithOpts(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
// in cache
- _, err = lkv.Get(context.TODO(), "k", clientv3.WithKeysOnly())
+ _, err = lkv.Get(t.Context(), "k", clientv3.WithKeysOnly())
require.NoError(t, err)
clus.Members[0].Stop(t)
@@ -310,7 +303,7 @@ func TestLeasingGetWithOpts(t *testing.T) {
clientv3.WithSerializable(),
}
for _, opt := range opts {
- _, err = lkv.Get(context.TODO(), "k", opt)
+ _, err = lkv.Get(t.Context(), "k", opt)
require.NoError(t, err)
}
@@ -319,15 +312,15 @@ func TestLeasingGetWithOpts(t *testing.T) {
getOpts = append(getOpts, opts[rand.Intn(len(opts))])
}
getOpts = getOpts[:rand.Intn(len(opts))]
- _, err = lkv.Get(context.TODO(), "k", getOpts...)
+ _, err = lkv.Get(t.Context(), "k", getOpts...)
require.NoError(t, err)
}
// TestLeasingConcurrentPut ensures that a get after concurrent puts returns
// the recently put data.
func TestLeasingConcurrentPut(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -335,7 +328,7 @@ func TestLeasingConcurrentPut(t *testing.T) {
defer closeLKV()
// force key into leasing key cache
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
// concurrently put through leasing client
@@ -343,7 +336,7 @@ func TestLeasingConcurrentPut(t *testing.T) {
putc := make(chan *clientv3.PutResponse, numPuts)
for i := 0; i < numPuts; i++ {
go func() {
- resp, perr := lkv.Put(context.TODO(), "k", "abc")
+ resp, perr := lkv.Put(t.Context(), "k", "abc")
if perr != nil {
t.Error(perr)
}
@@ -359,7 +352,7 @@ func TestLeasingConcurrentPut(t *testing.T) {
}
// confirm Get gives most recently put revisions
- getResp, gerr := lkv.Get(context.TODO(), "k")
+ getResp, gerr := lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
if mr := getResp.Kvs[0].ModRevision; mr != maxRev {
t.Errorf("expected ModRevision %d, got %d", maxRev, mr)
@@ -370,24 +363,24 @@ func TestLeasingConcurrentPut(t *testing.T) {
}
func TestLeasingDisconnectedGet(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "cached", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "cached", "abc")
require.NoError(t, err)
// get key so it's cached
- _, err = lkv.Get(context.TODO(), "cached")
+ _, err = lkv.Get(t.Context(), "cached")
require.NoError(t, err)
clus.Members[0].Stop(t)
// leasing key ownership should have "cached" locally served
- cachedResp, err := lkv.Get(context.TODO(), "cached")
+ cachedResp, err := lkv.Get(t.Context(), "cached")
require.NoError(t, err)
if len(cachedResp.Kvs) != 1 || string(cachedResp.Kvs[0].Value) != "abc" {
t.Fatalf(`expected "cached"->"abc", got response %+v`, cachedResp)
@@ -395,36 +388,34 @@ func TestLeasingDisconnectedGet(t *testing.T) {
}
func TestLeasingDeleteOwner(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
// get+own / delete / get
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
- _, err = lkv.Delete(context.TODO(), "k")
+ _, err = lkv.Delete(t.Context(), "k")
require.NoError(t, err)
- resp, err := lkv.Get(context.TODO(), "k")
+ resp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf(`expected "k" to be deleted, got response %+v`, resp)
- }
+ require.Emptyf(t, resp.Kvs, `expected "k" to be deleted, got response %+v`, resp)
// try to double delete
- _, err = lkv.Delete(context.TODO(), "k")
+ _, err = lkv.Delete(t.Context(), "k")
require.NoError(t, err)
}
func TestLeasingDeleteNonOwner(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv1, closeLKV1, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -435,42 +426,40 @@ func TestLeasingDeleteNonOwner(t *testing.T) {
require.NoError(t, err)
defer closeLKV2()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
// acquire ownership
- _, err = lkv1.Get(context.TODO(), "k")
+ _, err = lkv1.Get(t.Context(), "k")
require.NoError(t, err)
// delete via non-owner
- _, err = lkv2.Delete(context.TODO(), "k")
+ _, err = lkv2.Delete(t.Context(), "k")
require.NoError(t, err)
// key should be removed from lkv1
- resp, err := lkv1.Get(context.TODO(), "k")
+ resp, err := lkv1.Get(t.Context(), "k")
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf(`expected "k" to be deleted, got response %+v`, resp)
- }
+ require.Emptyf(t, resp.Kvs, `expected "k" to be deleted, got response %+v`, resp)
}
func TestLeasingOverwriteResponse(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
- resp, err := lkv.Get(context.TODO(), "k")
+ resp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
resp.Kvs[0].Key[0] = 'z'
resp.Kvs[0].Value[0] = 'z'
- resp, err = lkv.Get(context.TODO(), "k")
+ resp, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
if string(resp.Kvs[0].Key) != "k" {
@@ -482,19 +471,19 @@ func TestLeasingOverwriteResponse(t *testing.T) {
}
func TestLeasingOwnerPutResponse(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
- _, gerr := lkv.Get(context.TODO(), "k")
+ _, gerr := lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
- presp, err := lkv.Put(context.TODO(), "k", "def")
+ presp, err := lkv.Put(t.Context(), "k", "def")
require.NoError(t, err)
if presp == nil {
t.Fatal("expected put response, got nil")
@@ -502,7 +491,7 @@ func TestLeasingOwnerPutResponse(t *testing.T) {
clus.Members[0].Stop(t)
- gresp, gerr := lkv.Get(context.TODO(), "k")
+ gresp, gerr := lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
if gresp.Kvs[0].ModRevision != presp.Header.Revision {
t.Errorf("expected mod revision %d, got %d", presp.Header.Revision, gresp.Kvs[0].ModRevision)
@@ -513,8 +502,8 @@ func TestLeasingOwnerPutResponse(t *testing.T) {
}
func TestLeasingTxnOwnerGetRange(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -524,22 +513,21 @@ func TestLeasingTxnOwnerGetRange(t *testing.T) {
keyCount := rand.Intn(10) + 1
for i := 0; i < keyCount; i++ {
k := fmt.Sprintf("k-%d", i)
- _, err = clus.Client(0).Put(context.TODO(), k, k+k)
+ _, err = clus.Client(0).Put(t.Context(), k, k+k)
require.NoError(t, err)
}
- _, err = lkv.Get(context.TODO(), "k-")
+ _, err = lkv.Get(t.Context(), "k-")
require.NoError(t, err)
- tresp, terr := lkv.Txn(context.TODO()).Then(clientv3.OpGet("k-", clientv3.WithPrefix())).Commit()
+ tresp, terr := lkv.Txn(t.Context()).Then(clientv3.OpGet("k-", clientv3.WithPrefix())).Commit()
require.NoError(t, terr)
- if resp := tresp.Responses[0].GetResponseRange(); len(resp.Kvs) != keyCount {
- t.Fatalf("expected %d keys, got response %+v", keyCount, resp.Kvs)
- }
+ resp := tresp.Responses[0].GetResponseRange()
+ require.Equalf(t, len(resp.Kvs), keyCount, "expected %d keys, got response %+v", keyCount, resp.Kvs)
}
func TestLeasingTxnOwnerGet(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
client := clus.Client(0)
@@ -564,11 +552,11 @@ func TestLeasingTxnOwnerGet(t *testing.T) {
presps := make([]*clientv3.PutResponse, keyCount)
for i := range presps {
k := fmt.Sprintf("k-%d", i)
- presp, err := client.Put(context.TODO(), k, k+k)
+ presp, err := client.Put(t.Context(), k, k+k)
require.NoError(t, err)
presps[i] = presp
- _, err = lkv.Get(context.TODO(), k)
+ _, err = lkv.Get(t.Context(), k)
require.NoError(t, err)
ops = append(ops, clientv3.OpGet(k))
}
@@ -590,28 +578,20 @@ func TestLeasingTxnOwnerGet(t *testing.T) {
elseOps = ops
}
- tresp, terr := lkv.Txn(context.TODO()).
+ tresp, terr := lkv.Txn(t.Context()).
If(cmps...).
Then(thenOps...).
Else(elseOps...).Commit()
require.NoError(t, terr)
- if tresp.Succeeded != useThen {
- t.Fatalf("expected succeeded=%v, got tresp=%+v", useThen, tresp)
- }
- if len(tresp.Responses) != len(ops) {
- t.Fatalf("expected %d responses, got %d", len(ops), len(tresp.Responses))
- }
+ require.Equalf(t, tresp.Succeeded, useThen, "expected succeeded=%v, got tresp=%+v", useThen, tresp)
+ require.Lenf(t, ops, len(tresp.Responses), "expected %d responses, got %d", len(ops), len(tresp.Responses))
wrev := presps[len(presps)-1].Header.Revision
- if tresp.Header.Revision < wrev {
- t.Fatalf("expected header revision >= %d, got %d", wrev, tresp.Header.Revision)
- }
+ require.GreaterOrEqualf(t, tresp.Header.Revision, wrev, "expected header revision >= %d, got %d", wrev, tresp.Header.Revision)
for i := range ops {
k := fmt.Sprintf("k-%d", i)
rr := tresp.Responses[i].GetResponseRange()
- if rr == nil {
- t.Fatalf("expected get response, got %+v", tresp.Responses[i])
- }
+ require.NotNilf(t, rr, "expected get response, got %+v", tresp.Responses[i])
if string(rr.Kvs[0].Key) != k || string(rr.Kvs[0].Value) != k+k {
t.Errorf(`expected key for %q, got %+v`, k, rr.Kvs)
}
@@ -619,8 +599,8 @@ func TestLeasingTxnOwnerGet(t *testing.T) {
}
func TestLeasingTxnOwnerDeleteRange(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -630,65 +610,59 @@ func TestLeasingTxnOwnerDeleteRange(t *testing.T) {
keyCount := rand.Intn(10) + 1
for i := 0; i < keyCount; i++ {
k := fmt.Sprintf("k-%d", i)
- _, perr := clus.Client(0).Put(context.TODO(), k, k+k)
+ _, perr := clus.Client(0).Put(t.Context(), k, k+k)
require.NoError(t, perr)
}
// cache in lkv
- resp, err := lkv.Get(context.TODO(), "k-", clientv3.WithPrefix())
+ resp, err := lkv.Get(t.Context(), "k-", clientv3.WithPrefix())
require.NoError(t, err)
- if len(resp.Kvs) != keyCount {
- t.Fatalf("expected %d keys, got %d", keyCount, len(resp.Kvs))
- }
+ require.Equalf(t, len(resp.Kvs), keyCount, "expected %d keys, got %d", keyCount, len(resp.Kvs))
- _, terr := lkv.Txn(context.TODO()).Then(clientv3.OpDelete("k-", clientv3.WithPrefix())).Commit()
+ _, terr := lkv.Txn(t.Context()).Then(clientv3.OpDelete("k-", clientv3.WithPrefix())).Commit()
require.NoError(t, terr)
- resp, err = lkv.Get(context.TODO(), "k-", clientv3.WithPrefix())
+ resp, err = lkv.Get(t.Context(), "k-", clientv3.WithPrefix())
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf("expected no keys, got %d", len(resp.Kvs))
- }
+ require.Emptyf(t, resp.Kvs, "expected no keys, got %d", len(resp.Kvs))
}
func TestLeasingTxnOwnerDelete(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
// cache in lkv
- _, gerr := lkv.Get(context.TODO(), "k")
+ _, gerr := lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
- _, terr := lkv.Txn(context.TODO()).Then(clientv3.OpDelete("k")).Commit()
+ _, terr := lkv.Txn(t.Context()).Then(clientv3.OpDelete("k")).Commit()
require.NoError(t, terr)
- resp, err := lkv.Get(context.TODO(), "k")
+ resp, err := lkv.Get(t.Context(), "k")
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf("expected no keys, got %d", len(resp.Kvs))
- }
+ require.Emptyf(t, resp.Kvs, "expected no keys, got %d", len(resp.Kvs))
}
func TestLeasingTxnOwnerIf(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
// served through cache
@@ -756,7 +730,7 @@ func TestLeasingTxnOwnerIf(t *testing.T) {
}
for i, tt := range tests {
- tresp, terr := lkv.Txn(context.TODO()).If(tt.cmps...).Then(clientv3.OpGet("k")).Commit()
+ tresp, terr := lkv.Txn(t.Context()).If(tt.cmps...).Then(clientv3.OpGet("k")).Commit()
require.NoError(t, terr)
if tresp.Succeeded != tt.wSucceeded {
t.Errorf("#%d: expected succeeded %v, got %v", i, tt.wSucceeded, tresp.Succeeded)
@@ -768,8 +742,8 @@ func TestLeasingTxnOwnerIf(t *testing.T) {
}
func TestLeasingTxnCancel(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv1, closeLKV1, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -781,27 +755,26 @@ func TestLeasingTxnCancel(t *testing.T) {
defer closeLKV2()
// acquire lease but disconnect so no revoke in time
- _, err = lkv1.Get(context.TODO(), "k")
+ _, err = lkv1.Get(t.Context(), "k")
require.NoError(t, err)
clus.Members[0].Stop(t)
// wait for leader election, if any
- _, err = clus.Client(1).Get(context.TODO(), "abc")
+ _, err = clus.Client(1).Get(t.Context(), "abc")
require.NoError(t, err)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
go func() {
time.Sleep(100 * time.Millisecond)
cancel()
}()
- if _, err := lkv2.Txn(ctx).Then(clientv3.OpPut("k", "v")).Commit(); !errors.Is(err, context.Canceled) {
- t.Fatalf("expected %v, got %v", context.Canceled, err)
- }
+ _, err = lkv2.Txn(ctx).Then(clientv3.OpPut("k", "v")).Commit()
+ require.ErrorIsf(t, err, context.Canceled, "expected %v, got %v", context.Canceled, err)
}
func TestLeasingTxnNonOwnerPut(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -812,19 +785,19 @@ func TestLeasingTxnNonOwnerPut(t *testing.T) {
require.NoError(t, err)
defer closeLKV2()
- _, err = clus.Client(0).Put(context.TODO(), "k", "abc")
+ _, err = clus.Client(0).Put(t.Context(), "k", "abc")
require.NoError(t, err)
- _, err = clus.Client(0).Put(context.TODO(), "k2", "123")
+ _, err = clus.Client(0).Put(t.Context(), "k2", "123")
require.NoError(t, err)
// cache in lkv
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
- _, err = lkv.Get(context.TODO(), "k2")
+ _, err = lkv.Get(t.Context(), "k2")
require.NoError(t, err)
// invalidate via lkv2 txn
opArray := make([]clientv3.Op, 0)
opArray = append(opArray, clientv3.OpPut("k2", "456"))
- tresp, terr := lkv2.Txn(context.TODO()).Then(
+ tresp, terr := lkv2.Txn(t.Context()).Then(
clientv3.OpTxn(nil, opArray, nil),
clientv3.OpPut("k", "def"),
clientv3.OpPut("k3", "999"), // + a key not in any cache
@@ -834,12 +807,12 @@ func TestLeasingTxnNonOwnerPut(t *testing.T) {
t.Fatalf("expected txn success, got %+v", tresp)
}
// check cache was invalidated
- gresp, gerr := lkv.Get(context.TODO(), "k")
+ gresp, gerr := lkv.Get(t.Context(), "k")
require.NoError(t, gerr)
if len(gresp.Kvs) != 1 || string(gresp.Kvs[0].Value) != "def" {
t.Errorf(`expected value "def", got %+v`, gresp)
}
- gresp, gerr = lkv.Get(context.TODO(), "k2")
+ gresp, gerr = lkv.Get(t.Context(), "k2")
require.NoError(t, gerr)
if len(gresp.Kvs) != 1 || string(gresp.Kvs[0].Value) != "456" {
t.Errorf(`expected value "def", got %+v`, gresp)
@@ -853,23 +826,21 @@ func TestLeasingTxnNonOwnerPut(t *testing.T) {
clientv3.WithPrefix())
wresp := <-w
c := 0
- var evs []clientv3.Event
+ var evsInStr []string
for _, ev := range wresp.Events {
- evs = append(evs, *ev)
+ evsInStr = append(evsInStr, ev.String())
if ev.Kv.ModRevision == tresp.Header.Revision {
c++
}
}
- if c != 3 {
- t.Fatalf("expected 3 put events, got %+v", evs)
- }
+ require.Equalf(t, 3, c, "expected 3 put events, got %+v", evsInStr)
}
// TestLeasingTxnRandIfThenOrElse randomly leases keys two separate clients, then
// issues a random If/{Then,Else} transaction on those keys to one client.
func TestLeasingTxnRandIfThenOrElse(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv1, closeLKV1, err1 := leasing.NewKV(clus.Client(0), "pfx/")
@@ -884,7 +855,7 @@ func TestLeasingTxnRandIfThenOrElse(t *testing.T) {
dat := make([]*clientv3.PutResponse, keyCount)
for i := 0; i < keyCount; i++ {
k, v := fmt.Sprintf("k-%d", i), fmt.Sprintf("%d", i)
- dat[i], err1 = clus.Client(0).Put(context.TODO(), k, v)
+ dat[i], err1 = clus.Client(0).Put(t.Context(), k, v)
require.NoError(t, err1)
}
@@ -895,7 +866,7 @@ func TestLeasingTxnRandIfThenOrElse(t *testing.T) {
defer wg.Done()
for i := 0; i < keyCount/2; i++ {
k := fmt.Sprintf("k-%d", rand.Intn(keyCount))
- if _, err := kv.Get(context.TODO(), k); err != nil {
+ if _, err := kv.Get(t.Context(), k); err != nil {
t.Error(err)
}
getc <- struct{}{}
@@ -943,19 +914,17 @@ func TestLeasingTxnRandIfThenOrElse(t *testing.T) {
elseOps = ops
}
- tresp, terr := lkv1.Txn(context.TODO()).If(cmps...).Then(thenOps...).Else(elseOps...).Commit()
+ tresp, terr := lkv1.Txn(t.Context()).If(cmps...).Then(thenOps...).Else(elseOps...).Commit()
require.NoError(t, terr)
// cmps always succeed
- if tresp.Succeeded != useThen {
- t.Fatalf("expected succeeded=%v, got tresp=%+v", useThen, tresp)
- }
+ require.Equalf(t, tresp.Succeeded, useThen, "expected succeeded=%v, got tresp=%+v", useThen, tresp)
// get should match what was put
checkPuts := func(s string, kv clientv3.KV) {
for _, op := range ops {
if !op.IsPut() {
continue
}
- resp, rerr := kv.Get(context.TODO(), string(op.KeyBytes()))
+ resp, rerr := kv.Get(t.Context(), string(op.KeyBytes()))
require.NoError(t, rerr)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "a" {
t.Fatalf(`%s: expected value="a", got %+v`, s, resp.Kvs)
@@ -968,48 +937,46 @@ func TestLeasingTxnRandIfThenOrElse(t *testing.T) {
}
func TestLeasingOwnerPutError(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
defer cancel()
- if resp, err := lkv.Put(ctx, "k", "v"); err == nil {
- t.Fatalf("expected error, got response %+v", resp)
- }
+ resp, err := lkv.Put(ctx, "k", "v")
+ require.Errorf(t, err, "expected error, got response %+v", resp)
}
func TestLeasingOwnerDeleteError(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
defer cancel()
- if resp, err := lkv.Delete(ctx, "k"); err == nil {
- t.Fatalf("expected error, got response %+v", resp)
- }
+ resp, err := lkv.Delete(ctx, "k")
+ require.Errorf(t, err, "expected error, got response %+v", resp)
}
func TestLeasingNonOwnerPutError(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "pfx/")
@@ -1017,11 +984,10 @@ func TestLeasingNonOwnerPutError(t *testing.T) {
defer closeLKV()
clus.Members[0].Stop(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
+ ctx, cancel := context.WithTimeout(t.Context(), 100*time.Millisecond)
defer cancel()
- if resp, err := lkv.Put(ctx, "k", "v"); err == nil {
- t.Fatalf("expected error, got response %+v", resp)
- }
+ resp, err := lkv.Put(ctx, "k", "v")
+ require.Errorf(t, err, "expected error, got response %+v", resp)
}
func TestLeasingOwnerDeletePrefix(t *testing.T) {
@@ -1033,8 +999,8 @@ func TestLeasingOwnerDeleteFrom(t *testing.T) {
}
func testLeasingOwnerDelete(t *testing.T, del clientv3.Op) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "0/")
@@ -1042,24 +1008,22 @@ func testLeasingOwnerDelete(t *testing.T, del clientv3.Op) {
defer closeLKV()
for i := 0; i < 8; i++ {
- _, err = clus.Client(0).Put(context.TODO(), fmt.Sprintf("key/%d", i), "123")
+ _, err = clus.Client(0).Put(t.Context(), fmt.Sprintf("key/%d", i), "123")
require.NoError(t, err)
}
- _, err = lkv.Get(context.TODO(), "key/1")
+ _, err = lkv.Get(t.Context(), "key/1")
require.NoError(t, err)
- opResp, delErr := lkv.Do(context.TODO(), del)
+ opResp, delErr := lkv.Do(t.Context(), del)
require.NoError(t, delErr)
delResp := opResp.Del()
// confirm keys are invalidated from cache and deleted on etcd
for i := 0; i < 8; i++ {
- resp, err := lkv.Get(context.TODO(), fmt.Sprintf("key/%d", i))
+ resp, err := lkv.Get(t.Context(), fmt.Sprintf("key/%d", i))
require.NoError(t, err)
- if len(resp.Kvs) != 0 {
- t.Fatalf("expected no keys on key/%d, got %+v", i, resp)
- }
+ require.Emptyf(t, resp.Kvs, "expected no keys on key/%d, got %+v", i, resp)
}
// confirm keys were deleted atomically
@@ -1070,14 +1034,13 @@ func testLeasingOwnerDelete(t *testing.T, del clientv3.Op) {
clientv3.WithRev(delResp.Header.Revision),
clientv3.WithPrefix())
- if wresp := <-w; len(wresp.Events) != 8 {
- t.Fatalf("expected %d delete events,got %d", 8, len(wresp.Events))
- }
+ wresp := <-w
+ require.Lenf(t, wresp.Events, 8, "expected %d delete events,got %d", 8, len(wresp.Events))
}
func TestLeasingDeleteRangeBounds(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
delkv, closeDelKV, err := leasing.NewKV(clus.Client(0), "0/")
@@ -1089,30 +1052,28 @@ func TestLeasingDeleteRangeBounds(t *testing.T) {
defer closeGetKv()
for _, k := range []string{"j", "m"} {
- _, err = clus.Client(0).Put(context.TODO(), k, "123")
+ _, err = clus.Client(0).Put(t.Context(), k, "123")
require.NoError(t, err)
- _, err = getkv.Get(context.TODO(), k)
+ _, err = getkv.Get(t.Context(), k)
require.NoError(t, err)
}
- _, err = delkv.Delete(context.TODO(), "k", clientv3.WithPrefix())
+ _, err = delkv.Delete(t.Context(), "k", clientv3.WithPrefix())
require.NoError(t, err)
// leases still on server?
for _, k := range []string{"j", "m"} {
- resp, geterr := clus.Client(0).Get(context.TODO(), "0/"+k, clientv3.WithPrefix())
+ resp, geterr := clus.Client(0).Get(t.Context(), "0/"+k, clientv3.WithPrefix())
require.NoError(t, geterr)
- if len(resp.Kvs) != 1 {
- t.Fatalf("expected leasing key, got %+v", resp)
- }
+ require.Lenf(t, resp.Kvs, 1, "expected leasing key, got %+v", resp)
}
// j and m should still have leases registered since not under k*
clus.Members[0].Stop(t)
- _, err = getkv.Get(context.TODO(), "j")
+ _, err = getkv.Get(t.Context(), "j")
require.NoError(t, err)
- _, err = getkv.Get(context.TODO(), "m")
+ _, err = getkv.Get(t.Context(), "m")
require.NoError(t, err)
}
@@ -1127,8 +1088,8 @@ func TestLeaseDeleteRangeContendDel(t *testing.T) {
}
func testLeasingDeleteRangeContend(t *testing.T, op clientv3.Op) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
delkv, closeDelKV, err := leasing.NewKV(clus.Client(0), "0/")
@@ -1142,28 +1103,28 @@ func testLeasingDeleteRangeContend(t *testing.T, op clientv3.Op) {
const maxKey = 8
for i := 0; i < maxKey; i++ {
key := fmt.Sprintf("key/%d", i)
- _, err = clus.Client(0).Put(context.TODO(), key, "123")
+ _, err = clus.Client(0).Put(t.Context(), key, "123")
require.NoError(t, err)
- _, err = putkv.Get(context.TODO(), key)
+ _, err = putkv.Get(t.Context(), key)
require.NoError(t, err)
}
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
donec := make(chan struct{})
go func(t *testing.T) {
defer close(donec)
for i := 0; ctx.Err() == nil; i++ {
key := fmt.Sprintf("key/%d", i%maxKey)
- if _, err = putkv.Put(context.TODO(), key, "123"); err != nil {
+ if _, err = putkv.Put(t.Context(), key, "123"); err != nil {
t.Errorf("fail putting key %s: %v", key, err)
}
- if _, err = putkv.Get(context.TODO(), key); err != nil {
+ if _, err = putkv.Get(t.Context(), key); err != nil {
t.Errorf("fail getting key %s: %v", key, err)
}
}
}(t)
- _, delErr := delkv.Do(context.TODO(), op)
+ _, delErr := delkv.Do(t.Context(), op)
cancel()
<-donec
require.NoError(t, delErr)
@@ -1171,9 +1132,9 @@ func testLeasingDeleteRangeContend(t *testing.T, op clientv3.Op) {
// confirm keys on non-deleter match etcd
for i := 0; i < maxKey; i++ {
key := fmt.Sprintf("key/%d", i)
- resp, err := putkv.Get(context.TODO(), key)
+ resp, err := putkv.Get(t.Context(), key)
require.NoError(t, err)
- servResp, err := clus.Client(0).Get(context.TODO(), key)
+ servResp, err := clus.Client(0).Get(t.Context(), key)
require.NoError(t, err)
if !reflect.DeepEqual(resp.Kvs, servResp.Kvs) {
t.Errorf("#%d: expected %+v, got %+v", i, servResp.Kvs, resp.Kvs)
@@ -1182,8 +1143,8 @@ func testLeasingDeleteRangeContend(t *testing.T, op clientv3.Op) {
}
func TestLeasingPutGetDeleteConcurrent(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkvs := make([]clientv3.KV, 16)
@@ -1195,12 +1156,12 @@ func TestLeasingPutGetDeleteConcurrent(t *testing.T) {
}
getdel := func(kv clientv3.KV) {
- _, err := kv.Put(context.TODO(), "k", "abc")
+ _, err := kv.Put(t.Context(), "k", "abc")
require.NoError(t, err)
time.Sleep(time.Millisecond)
- _, err = kv.Get(context.TODO(), "k")
+ _, err = kv.Get(t.Context(), "k")
require.NoError(t, err)
- _, err = kv.Delete(context.TODO(), "k")
+ _, err = kv.Delete(t.Context(), "k")
require.NoError(t, err)
time.Sleep(2 * time.Millisecond)
}
@@ -1217,25 +1178,21 @@ func TestLeasingPutGetDeleteConcurrent(t *testing.T) {
}
wg.Wait()
- resp, err := lkvs[0].Get(context.TODO(), "k")
+ resp, err := lkvs[0].Get(t.Context(), "k")
require.NoError(t, err)
- if len(resp.Kvs) > 0 {
- t.Fatalf("expected no kvs, got %+v", resp.Kvs)
- }
+ require.Emptyf(t, resp.Kvs, "expected no kvs, got %+v", resp.Kvs)
- resp, err = clus.Client(0).Get(context.TODO(), "k")
+ resp, err = clus.Client(0).Get(t.Context(), "k")
require.NoError(t, err)
- if len(resp.Kvs) > 0 {
- t.Fatalf("expected no kvs, got %+v", resp.Kvs)
- }
+ require.Emptyf(t, resp.Kvs, "expected no kvs, got %+v", resp.Kvs)
}
// TestLeasingReconnectOwnerRevoke checks that revocation works if
// disconnected when trying to submit revoke txn.
func TestLeasingReconnectOwnerRevoke(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv1, closeLKV1, err1 := leasing.NewKV(clus.Client(0), "foo/")
@@ -1246,7 +1203,7 @@ func TestLeasingReconnectOwnerRevoke(t *testing.T) {
require.NoError(t, err2)
defer closeLKV2()
- _, err := lkv1.Get(context.TODO(), "k")
+ _, err := lkv1.Get(t.Context(), "k")
require.NoError(t, err)
// force leader away from member 0
@@ -1254,7 +1211,7 @@ func TestLeasingReconnectOwnerRevoke(t *testing.T) {
clus.WaitLeader(t)
clus.Members[0].Restart(t)
- cctx, cancel := context.WithCancel(context.TODO())
+ cctx, cancel := context.WithCancel(t.Context())
sdonec, pdonec := make(chan struct{}), make(chan struct{})
// make lkv1 connection choppy so Txn fails
go func() {
@@ -1294,8 +1251,8 @@ func TestLeasingReconnectOwnerRevoke(t *testing.T) {
// TestLeasingReconnectOwnerRevokeCompact checks that revocation works if
// disconnected and the watch is compacted.
func TestLeasingReconnectOwnerRevokeCompact(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv1, closeLKV1, err1 := leasing.NewKV(clus.Client(0), "foo/")
@@ -1306,51 +1263,49 @@ func TestLeasingReconnectOwnerRevokeCompact(t *testing.T) {
require.NoError(t, err2)
defer closeLKV2()
- _, err := lkv1.Get(context.TODO(), "k")
+ _, err := lkv1.Get(t.Context(), "k")
require.NoError(t, err)
clus.Members[0].Stop(t)
clus.WaitLeader(t)
// put some more revisions for compaction
- _, err = clus.Client(1).Put(context.TODO(), "a", "123")
+ _, err = clus.Client(1).Put(t.Context(), "a", "123")
require.NoError(t, err)
- presp, err := clus.Client(1).Put(context.TODO(), "a", "123")
+ presp, err := clus.Client(1).Put(t.Context(), "a", "123")
require.NoError(t, err)
// compact while lkv1 is disconnected
rev := presp.Header.Revision
- _, err = clus.Client(1).Compact(context.TODO(), rev)
+ _, err = clus.Client(1).Compact(t.Context(), rev)
require.NoError(t, err)
clus.Members[0].Restart(t)
- cctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ cctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
_, err = lkv2.Put(cctx, "k", "v")
require.NoError(t, err)
resp, err := lkv1.Get(cctx, "k")
require.NoError(t, err)
- if string(resp.Kvs[0].Value) != "v" {
- t.Fatalf(`expected "v" value, got %+v`, resp)
- }
+ require.Equalf(t, "v", string(resp.Kvs[0].Value), `expected "v" value, got %+v`, resp)
}
// TestLeasingReconnectOwnerConsistency checks a write error on an owner will
// not cause inconsistency between the server and the client.
func TestLeasingReconnectOwnerConsistency(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
defer closeLKV()
require.NoError(t, err)
- _, err = lkv.Put(context.TODO(), "k", "x")
+ _, err = lkv.Put(t.Context(), "k", "x")
require.NoError(t, err)
- _, err = lkv.Put(context.TODO(), "kk", "y")
+ _, err = lkv.Put(t.Context(), "kk", "y")
require.NoError(t, err)
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
for i := 0; i < 10; i++ {
@@ -1366,27 +1321,27 @@ func TestLeasingReconnectOwnerConsistency(t *testing.T) {
}()
switch rand.Intn(7) {
case 0:
- _, err = lkv.Put(context.TODO(), "k", v)
+ _, err = lkv.Put(t.Context(), "k", v)
case 1:
- _, err = lkv.Delete(context.TODO(), "k")
+ _, err = lkv.Delete(t.Context(), "k")
case 2:
- txn := lkv.Txn(context.TODO()).Then(
+ txn := lkv.Txn(t.Context()).Then(
clientv3.OpGet("k"),
clientv3.OpDelete("k"),
)
_, err = txn.Commit()
case 3:
- txn := lkv.Txn(context.TODO()).Then(
+ txn := lkv.Txn(t.Context()).Then(
clientv3.OpGet("k"),
clientv3.OpPut("k", v),
)
_, err = txn.Commit()
case 4:
- _, err = lkv.Do(context.TODO(), clientv3.OpPut("k", v))
+ _, err = lkv.Do(t.Context(), clientv3.OpPut("k", v))
case 5:
- _, err = lkv.Do(context.TODO(), clientv3.OpDelete("k"))
+ _, err = lkv.Do(t.Context(), clientv3.OpDelete("k"))
case 6:
- _, err = lkv.Delete(context.TODO(), "k", clientv3.WithPrefix())
+ _, err = lkv.Delete(t.Context(), "k", clientv3.WithPrefix())
}
<-donec
if err != nil {
@@ -1395,18 +1350,16 @@ func TestLeasingReconnectOwnerConsistency(t *testing.T) {
}
}
- lresp, lerr := lkv.Get(context.TODO(), "k")
+ lresp, lerr := lkv.Get(t.Context(), "k")
require.NoError(t, lerr)
- cresp, cerr := clus.Client(0).Get(context.TODO(), "k")
+ cresp, cerr := clus.Client(0).Get(t.Context(), "k")
require.NoError(t, cerr)
- if !reflect.DeepEqual(lresp.Kvs, cresp.Kvs) {
- t.Fatalf("expected %+v, got %+v", cresp, lresp)
- }
+ require.Truef(t, reflect.DeepEqual(lresp.Kvs, cresp.Kvs), "expected %+v, got %+v", cresp, lresp)
}
func TestLeasingTxnAtomicCache(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
@@ -1418,10 +1371,10 @@ func TestLeasingTxnAtomicCache(t *testing.T) {
k := fmt.Sprintf("k-%d", i)
puts[i], gets[i] = clientv3.OpPut(k, k), clientv3.OpGet(k)
}
- _, err = clus.Client(0).Txn(context.TODO()).Then(puts...).Commit()
+ _, err = clus.Client(0).Txn(t.Context()).Then(puts...).Commit()
require.NoError(t, err)
for i := range gets {
- _, err = lkv.Do(context.TODO(), gets[i])
+ _, err = lkv.Do(t.Context(), gets[i])
require.NoError(t, err)
}
@@ -1435,7 +1388,7 @@ func TestLeasingTxnAtomicCache(t *testing.T) {
f := func() {
defer wgPutters.Done()
for i := 0; i < 10; i++ {
- if _, txnerr := lkv.Txn(context.TODO()).Then(puts...).Commit(); txnerr != nil {
+ if _, txnerr := lkv.Txn(t.Context()).Then(puts...).Commit(); txnerr != nil {
select {
case txnerrCh <- txnerr:
default:
@@ -1453,7 +1406,7 @@ func TestLeasingTxnAtomicCache(t *testing.T) {
return
default:
}
- tresp, err := lkv.Txn(context.TODO()).Then(gets...).Commit()
+ tresp, err := lkv.Txn(t.Context()).Then(gets...).Commit()
if err != nil {
t.Error(err)
}
@@ -1489,15 +1442,15 @@ func TestLeasingTxnAtomicCache(t *testing.T) {
// TestLeasingReconnectTxn checks that Txn is resilient to disconnects.
func TestLeasingReconnectTxn(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
donec := make(chan struct{})
@@ -1511,7 +1464,7 @@ func TestLeasingReconnectTxn(t *testing.T) {
time.Sleep(10 * time.Millisecond)
}()
- _, lerr := lkv.Txn(context.TODO()).
+ _, lerr := lkv.Txn(t.Context()).
If(clientv3.Compare(clientv3.Version("k"), "=", 0)).
Then(clientv3.OpGet("k")).
Commit()
@@ -1522,8 +1475,8 @@ func TestLeasingReconnectTxn(t *testing.T) {
// TestLeasingReconnectNonOwnerGet checks a get error on an owner will
// not cause inconsistency between the server and the client.
func TestLeasingReconnectNonOwnerGet(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
@@ -1533,7 +1486,7 @@ func TestLeasingReconnectNonOwnerGet(t *testing.T) {
// populate a few keys so some leasing gets have keys
for i := 0; i < 4; i++ {
k := fmt.Sprintf("k-%d", i*2)
- _, err = lkv.Put(context.TODO(), k, k[2:])
+ _, err = lkv.Put(t.Context(), k, k[2:])
require.NoError(t, err)
}
@@ -1548,7 +1501,7 @@ func TestLeasingReconnectNonOwnerGet(t *testing.T) {
time.Sleep(time.Millisecond)
}
}()
- _, err = lkv.Get(context.TODO(), fmt.Sprintf("k-%d", i))
+ _, err = lkv.Get(t.Context(), fmt.Sprintf("k-%d", i))
<-donec
n++
if err != nil {
@@ -1557,48 +1510,44 @@ func TestLeasingReconnectNonOwnerGet(t *testing.T) {
}
for i := 0; i < n; i++ {
k := fmt.Sprintf("k-%d", i)
- lresp, lerr := lkv.Get(context.TODO(), k)
+ lresp, lerr := lkv.Get(t.Context(), k)
require.NoError(t, lerr)
- cresp, cerr := clus.Client(0).Get(context.TODO(), k)
+ cresp, cerr := clus.Client(0).Get(t.Context(), k)
require.NoError(t, cerr)
- if !reflect.DeepEqual(lresp.Kvs, cresp.Kvs) {
- t.Fatalf("expected %+v, got %+v", cresp, lresp)
- }
+ require.Truef(t, reflect.DeepEqual(lresp.Kvs, cresp.Kvs), "expected %+v, got %+v", cresp, lresp)
}
}
func TestLeasingTxnRangeCmp(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
require.NoError(t, err)
defer closeLKV()
- _, err = clus.Client(0).Put(context.TODO(), "k", "a")
+ _, err = clus.Client(0).Put(t.Context(), "k", "a")
require.NoError(t, err)
// k2 version = 2
- _, err = clus.Client(0).Put(context.TODO(), "k2", "a")
+ _, err = clus.Client(0).Put(t.Context(), "k2", "a")
require.NoError(t, err)
- _, err = clus.Client(0).Put(context.TODO(), "k2", "a")
+ _, err = clus.Client(0).Put(t.Context(), "k2", "a")
require.NoError(t, err)
// cache k
- _, err = lkv.Get(context.TODO(), "k")
+ _, err = lkv.Get(t.Context(), "k")
require.NoError(t, err)
cmp := clientv3.Compare(clientv3.Version("k").WithPrefix(), "=", 1)
- tresp, terr := lkv.Txn(context.TODO()).If(cmp).Commit()
+ tresp, terr := lkv.Txn(t.Context()).If(cmp).Commit()
require.NoError(t, terr)
- if tresp.Succeeded {
- t.Fatalf("expected Succeeded=false, got %+v", tresp)
- }
+ require.Falsef(t, tresp.Succeeded, "expected Succeeded=false, got %+v", tresp)
}
func TestLeasingDo(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
@@ -1613,7 +1562,7 @@ func TestLeasingDo(t *testing.T) {
clientv3.OpTxn(nil, nil, nil),
}
for i, op := range ops {
- resp, resperr := lkv.Do(context.TODO(), op)
+ resp, resperr := lkv.Do(t.Context(), op)
if resperr != nil {
t.Errorf("#%d: failed (%v)", i, resperr)
}
@@ -1629,16 +1578,14 @@ func TestLeasingDo(t *testing.T) {
}
}
- gresp, err := clus.Client(0).Get(context.TODO(), "a", clientv3.WithPrefix())
+ gresp, err := clus.Client(0).Get(t.Context(), "a", clientv3.WithPrefix())
require.NoError(t, err)
- if len(gresp.Kvs) != 0 {
- t.Fatalf("expected no keys, got %+v", gresp.Kvs)
- }
+ require.Emptyf(t, gresp.Kvs, "expected no keys, got %+v", gresp.Kvs)
}
func TestLeasingTxnOwnerPutBranch(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/")
@@ -1649,13 +1596,13 @@ func TestLeasingTxnOwnerPutBranch(t *testing.T) {
treeOp := makePutTreeOp("tree", &n, 4)
for i := 0; i < n; i++ {
k := fmt.Sprintf("tree/%d", i)
- _, err = clus.Client(0).Put(context.TODO(), k, "a")
+ _, err = clus.Client(0).Put(t.Context(), k, "a")
require.NoError(t, err)
- _, err = lkv.Get(context.TODO(), k)
+ _, err = lkv.Get(t.Context(), k)
require.NoError(t, err)
}
- _, err = lkv.Do(context.TODO(), treeOp)
+ _, err = lkv.Do(t.Context(), treeOp)
require.NoError(t, err)
// lkv shouldn't need to call out to server for updated leased keys
@@ -1663,13 +1610,11 @@ func TestLeasingTxnOwnerPutBranch(t *testing.T) {
for i := 0; i < n; i++ {
k := fmt.Sprintf("tree/%d", i)
- lkvResp, err := lkv.Get(context.TODO(), k)
+ lkvResp, err := lkv.Get(t.Context(), k)
require.NoError(t, err)
- clusResp, err := clus.Client(1).Get(context.TODO(), k)
+ clusResp, err := clus.Client(1).Get(t.Context(), k)
require.NoError(t, err)
- if !reflect.DeepEqual(clusResp.Kvs, lkvResp.Kvs) {
- t.Fatalf("expected %+v, got %+v", clusResp.Kvs, lkvResp.Kvs)
- }
+ require.Truef(t, reflect.DeepEqual(clusResp.Kvs, lkvResp.Kvs), "expected %+v, got %+v", clusResp.Kvs, lkvResp.Kvs)
}
}
@@ -1723,8 +1668,8 @@ func randCmps(pfx string, dat []*clientv3.PutResponse) (cmps []clientv3.Cmp, the
}
func TestLeasingSessionExpire(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/", concurrency.WithTTL(1))
@@ -1736,7 +1681,7 @@ func TestLeasingSessionExpire(t *testing.T) {
defer closeLKV2()
// acquire lease on abc
- _, err = lkv.Get(context.TODO(), "abc")
+ _, err = lkv.Get(t.Context(), "abc")
require.NoError(t, err)
// down endpoint lkv uses for keepalives
@@ -1745,15 +1690,13 @@ func TestLeasingSessionExpire(t *testing.T) {
require.NoError(t, err)
waitForExpireAck(t, lkv)
clus.Members[0].Restart(t)
- integration2.WaitClientV3(t, lkv2)
- _, err = lkv2.Put(context.TODO(), "abc", "def")
+ integration.WaitClientV3(t, lkv2)
+ _, err = lkv2.Put(t.Context(), "abc", "def")
require.NoError(t, err)
- resp, err := lkv.Get(context.TODO(), "abc")
+ resp, err := lkv.Get(t.Context(), "abc")
require.NoError(t, err)
- if v := string(resp.Kvs[0].Value); v != "def" {
- t.Fatalf("expected %q, got %q", "v", v)
- }
+ require.Equal(t, "def", string(resp.Kvs[0].Value))
}
func TestLeasingSessionExpireCancel(t *testing.T) {
@@ -1794,15 +1737,15 @@ func TestLeasingSessionExpireCancel(t *testing.T) {
}
for i := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
lkv, closeLKV, err := leasing.NewKV(clus.Client(0), "foo/", concurrency.WithTTL(1))
require.NoError(t, err)
defer closeLKV()
- _, err = lkv.Get(context.TODO(), "abc")
+ _, err = lkv.Get(t.Context(), "abc")
require.NoError(t, err)
// down endpoint lkv uses for keepalives
@@ -1811,7 +1754,7 @@ func TestLeasingSessionExpireCancel(t *testing.T) {
require.NoError(t, err)
waitForExpireAck(t, lkv)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
errc := make(chan error, 1)
go func() { errc <- tests[i](ctx, lkv) }()
// some delay to get past for ctx.Err() != nil {} loops
@@ -1848,7 +1791,7 @@ func waitForLeasingExpire(kv clientv3.KV, lkey string) error {
func waitForExpireAck(t *testing.T, kv clientv3.KV) {
// wait for leasing client to acknowledge lost lease
for i := 0; i < 10; i++ {
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
_, err := kv.Get(ctx, "abc")
cancel()
if errors.Is(err, ctx.Err()) {
diff --git a/tests/integration/clientv3/lease/main_test.go b/tests/integration/clientv3/lease/main_test.go
index 1057c4ab86e0..0064ea6adf0a 100644
--- a/tests/integration/clientv3/lease/main_test.go
+++ b/tests/integration/clientv3/lease/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 lease_test
diff --git a/tests/integration/clientv3/main_test.go b/tests/integration/clientv3/main_test.go
index 9481beebcb55..e1755896171a 100644
--- a/tests/integration/clientv3/main_test.go
+++ b/tests/integration/clientv3/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 clientv3test
diff --git a/tests/integration/clientv3/maintenance_test.go b/tests/integration/clientv3/maintenance_test.go
index 33dabfdc87b3..67e72395b6d2 100644
--- a/tests/integration/clientv3/maintenance_test.go
+++ b/tests/integration/clientv3/maintenance_test.go
@@ -30,7 +30,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
- "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
@@ -40,17 +41,17 @@ import (
"go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/mvcc"
"go.etcd.io/etcd/server/v3/storage/mvcc/testutil"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMaintenanceHashKV(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
for i := 0; i < 3; i++ {
- _, err := clus.RandClient().Put(context.Background(), "foo", "bar")
+ _, err := clus.RandClient().Put(t.Context(), "foo", "bar")
require.NoError(t, err)
}
@@ -58,9 +59,9 @@ func TestMaintenanceHashKV(t *testing.T) {
for i := 0; i < 3; i++ {
cli := clus.Client(i)
// ensure writes are replicated
- _, err := cli.Get(context.TODO(), "foo")
+ _, err := cli.Get(t.Context(), "foo")
require.NoError(t, err)
- hresp, err := cli.HashKV(context.Background(), clus.Members[i].GRPCURL, 0)
+ hresp, err := cli.HashKV(t.Context(), clus.Members[i].GRPCURL, 0)
require.NoError(t, err)
if hv == 0 {
hv = hresp.Hash
@@ -75,15 +76,15 @@ func TestMaintenanceHashKV(t *testing.T) {
// TestCompactionHash tests compaction hash
// TODO: Change this to fuzz test
func TestCompactionHash(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- testutil.TestCompactionHash(context.Background(), t, hashTestCase{cc, clus.Members[0].GRPCURL}, 1000)
+ testutil.TestCompactionHash(t.Context(), t, hashTestCase{cc, clus.Members[0].GRPCURL}, 1000)
}
type hashTestCase struct {
@@ -119,9 +120,9 @@ func (tc hashTestCase) Compact(ctx context.Context, rev int64) error {
}
func TestMaintenanceMoveLeader(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
oldLeadIdx := clus.WaitLeader(t)
@@ -129,13 +130,13 @@ func TestMaintenanceMoveLeader(t *testing.T) {
target := uint64(clus.Members[targetIdx].ID())
cli := clus.Client(targetIdx)
- _, err := cli.MoveLeader(context.Background(), target)
+ _, err := cli.MoveLeader(t.Context(), target)
if !errors.Is(err, rpctypes.ErrNotLeader) {
t.Fatalf("error expected %v, got %v", rpctypes.ErrNotLeader, err)
}
cli = clus.Client(oldLeadIdx)
- _, err = cli.MoveLeader(context.Background(), target)
+ _, err = cli.MoveLeader(t.Context(), target)
require.NoError(t, err)
leadIdx := clus.WaitLeader(t)
@@ -148,13 +149,13 @@ func TestMaintenanceMoveLeader(t *testing.T) {
// TestMaintenanceSnapshotCancel ensures that context cancel
// before snapshot reading returns corresponding context errors.
func TestMaintenanceSnapshotCancel(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
// reading snapshot with canceled context should error out
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
// Since http2 spec defines the receive windows's size and max size of
// frame in the stream, the underlayer - gRPC client can pre-read data
@@ -182,6 +183,25 @@ func TestMaintenanceSnapshotCancel(t *testing.T) {
}
}
+// TestMaintenanceSnapshotFromServerClient verifies that snapshot streams created
+// by Member.ServerClient (in-process adapter path) complete successfully.
+func TestMaintenanceSnapshotFromServerClient(t *testing.T) {
+ integration.BeforeTest(t)
+
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ srvClient := clus.Members[0].ServerClient
+ require.NotNilf(t, srvClient, "Member.ServerClient must be initialized")
+
+ rc, err := srvClient.Snapshot(t.Context())
+ require.NoError(t, err)
+ defer rc.Close()
+
+ _, err = io.Copy(io.Discard, rc)
+ require.NoErrorf(t, err, "snapshot stream should terminate cleanly")
+}
+
// TestMaintenanceSnapshotWithVersionTimeout ensures that SnapshotWithVersion function
// returns corresponding context errors when context timeout happened before snapshot reading
func TestMaintenanceSnapshotWithVersionTimeout(t *testing.T) {
@@ -205,13 +225,13 @@ func TestMaintenanceSnapshotTimeout(t *testing.T) {
// testMaintenanceSnapshotTimeout given snapshot function ensures that it
// returns corresponding context errors when context timeout happened before snapshot reading
func testMaintenanceSnapshotTimeout(t *testing.T, snapshot func(context.Context, *clientv3.Client) (io.ReadCloser, error)) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
// reading snapshot with deadline exceeded should error out
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
// Since http2 spec defines the receive windows's size and max size of
@@ -230,9 +250,22 @@ func testMaintenanceSnapshotTimeout(t *testing.T, snapshot func(context.Context,
time.Sleep(2 * time.Second)
_, err = io.Copy(io.Discard, rc2)
- if err != nil && !IsClientTimeout(err) {
- t.Errorf("expected client timeout, got %v", err)
+ if IsClientTimeout(err) {
+ return
}
+ // Assumes the client receives a single message header and then
+ // waits for the payload body. If the context is canceled before
+ // the payload arrives, the client will read io.EOF. However, the
+ // grpc-go client converts this into io.ErrUnexpectedEOF with an
+ // internal error code. Ideally, grpc-go might return context.Canceled
+ // instead, but it's unclear if that's feasible. Let's explicitly
+ // check for this error in the test code.
+ //
+ // REF: https://github.com/grpc/grpc-go/blob/6821606f351799b026fda1e6ba143315e6c1e620/rpc_util.go#L644
+ //
+ // Once https://github.com/grpc/grpc-go/issues/8281 is fixed, we should
+ // revert this change. See more discussion in https://github.com/etcd-io/etcd/pull/19833.
+ assert.ErrorIs(t, status.Error(codes.Internal, io.ErrUnexpectedEOF.Error()), err)
}
// TestMaintenanceSnapshotWithVersionErrorInflight ensures that ReaderCloser returned by SnapshotWithVersion function
@@ -258,10 +291,10 @@ func TestMaintenanceSnapshotErrorInflight(t *testing.T) {
// testMaintenanceSnapshotErrorInflight given snapshot function ensures that ReaderCloser returned by it
// will fail to read with corresponding context errors on inflight context cancel timeout.
func testMaintenanceSnapshotErrorInflight(t *testing.T, snapshot func(context.Context, *clientv3.Client) (io.ReadCloser, error)) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
lg := zaptest.NewLogger(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
// take about 1-second to read snapshot
@@ -278,7 +311,7 @@ func testMaintenanceSnapshotErrorInflight(t *testing.T, snapshot func(context.Co
clus.Members[0].Restart(t)
// reading snapshot with canceled context should error out
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
rc1, err := snapshot(ctx, clus.RandClient())
require.NoError(t, err)
defer rc1.Close()
@@ -296,7 +329,7 @@ func testMaintenanceSnapshotErrorInflight(t *testing.T, snapshot func(context.Co
<-donec
// reading snapshot with deadline exceeded should error out
- ctx, cancel = context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel = context.WithTimeout(t.Context(), time.Second)
defer cancel()
rc2, err := snapshot(ctx, clus.RandClient())
require.NoError(t, err)
@@ -312,36 +345,36 @@ func testMaintenanceSnapshotErrorInflight(t *testing.T, snapshot func(context.Co
// TestMaintenanceSnapshotWithVersionVersion ensures that SnapshotWithVersion returns correct version value.
func TestMaintenanceSnapshotWithVersionVersion(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// Set SnapshotCount to 1 to force raft snapshot to ensure that storage version is set
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, SnapshotCount: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, SnapshotCount: 1})
defer clus.Terminate(t)
// Put some keys to ensure that wal snapshot is triggered
for i := 0; i < 10; i++ {
- clus.RandClient().Put(context.Background(), fmt.Sprintf("%d", i), "1")
+ clus.RandClient().Put(t.Context(), fmt.Sprintf("%d", i), "1")
}
// reading snapshot with canceled context should error out
- resp, err := clus.RandClient().SnapshotWithVersion(context.Background())
+ resp, err := clus.RandClient().SnapshotWithVersion(t.Context())
require.NoError(t, err)
defer resp.Snapshot.Close()
- if resp.Version != "3.6.0" {
+ if resp.Version != "3.7.0" {
t.Errorf("unexpected version, expected %q, got %q", version.Version, resp.Version)
}
}
func TestMaintenanceSnapshotContentDigest(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
populateDataIntoCluster(t, clus, 3, 1024*1024)
// reading snapshot with canceled context should error out
- resp, err := clus.RandClient().SnapshotWithVersion(context.Background())
+ resp, err := clus.RandClient().SnapshotWithVersion(t.Context())
require.NoError(t, err)
defer resp.Snapshot.Close()
@@ -402,9 +435,9 @@ func TestMaintenanceStatus(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, QuotaBackendBytes: tc.quotaCfg})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, QuotaBackendBytes: tc.quotaCfg})
defer clus.Terminate(t)
t.Logf("Waiting for leader...")
@@ -417,7 +450,7 @@ func TestMaintenanceStatus(t *testing.T) {
}
t.Logf("Creating client...")
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: eps, DialOptions: []grpc.DialOption{grpc.WithBlock()}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: eps})
require.NoError(t, err)
defer cli.Close()
t.Logf("Creating client [DONE]")
diff --git a/tests/integration/clientv3/metrics_test.go b/tests/integration/clientv3/metrics_test.go
index 9d1d2e96f1e0..0115a4f981e8 100644
--- a/tests/integration/clientv3/metrics_test.go
+++ b/tests/integration/clientv3/metrics_test.go
@@ -17,7 +17,6 @@ package clientv3test
import (
"bufio"
"bytes"
- "context"
"errors"
"io"
"net"
@@ -35,11 +34,11 @@ import (
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestV3ClientMetrics(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
var (
addr = "localhost:27989"
@@ -73,7 +72,7 @@ func TestV3ClientMetrics(t *testing.T) {
url := "unix://" + addr + "/metrics"
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
clientMetrics := grpcprom.NewClientMetrics()
@@ -86,17 +85,17 @@ func TestV3ClientMetrics(t *testing.T) {
grpc.WithStreamInterceptor(clientMetrics.StreamClientInterceptor()),
},
}
- cli, cerr := integration2.NewClient(t, cfg)
+ cli, cerr := integration.NewClient(t, cfg)
require.NoError(t, cerr)
defer cli.Close()
- wc := cli.Watch(context.Background(), "foo")
+ wc := cli.Watch(t.Context(), "foo")
wBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream")
pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary")
- _, err = cli.Put(context.Background(), "foo", "bar")
+ _, err = cli.Put(t.Context(), "foo", "bar")
if err != nil {
t.Errorf("Error putting value in key store")
}
diff --git a/tests/integration/clientv3/mirror_auth_test.go b/tests/integration/clientv3/mirror_auth_test.go
index 1738fea5a644..f8e2a0446d86 100644
--- a/tests/integration/clientv3/mirror_auth_test.go
+++ b/tests/integration/clientv3/mirror_auth_test.go
@@ -17,35 +17,33 @@
package clientv3test
import (
- "context"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/mirror"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMirrorSync_Authenticated(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
initialClient := clus.Client(0)
// Create a user to run the mirror process that only has access to /syncpath
- initialClient.RoleAdd(context.Background(), "syncer")
- initialClient.RoleGrantPermission(context.Background(), "syncer", "/syncpath", clientv3.GetPrefixRangeEnd("/syncpath"), clientv3.PermissionType(clientv3.PermReadWrite))
- initialClient.UserAdd(context.Background(), "syncer", "syncfoo")
- initialClient.UserGrantRole(context.Background(), "syncer", "syncer")
+ initialClient.RoleAdd(t.Context(), "syncer")
+ initialClient.RoleGrantPermission(t.Context(), "syncer", "/syncpath", clientv3.GetPrefixRangeEnd("/syncpath"), clientv3.PermissionType(clientv3.PermReadWrite))
+ initialClient.UserAdd(t.Context(), "syncer", "syncfoo")
+ initialClient.UserGrantRole(t.Context(), "syncer", "syncer")
// Seed /syncpath with some initial data
- _, err := initialClient.KV.Put(context.TODO(), "/syncpath/foo", "bar")
+ _, err := initialClient.KV.Put(t.Context(), "/syncpath/foo", "bar")
require.NoError(t, err)
// Require authentication
@@ -55,17 +53,16 @@ func TestMirrorSync_Authenticated(t *testing.T) {
cfg := clientv3.Config{
Endpoints: initialClient.Endpoints(),
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
Username: "syncer",
Password: "syncfoo",
}
- syncClient, err := integration2.NewClient(t, cfg)
+ syncClient, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer syncClient.Close()
// Now run the sync process, create changes, and get the initial sync state
syncer := mirror.NewSyncer(syncClient, "/syncpath", 0)
- gch, ech := syncer.SyncBase(context.TODO())
+ gch, ech := syncer.SyncBase(t.Context())
wkvs := []*mvccpb.KeyValue{{Key: []byte("/syncpath/foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1}}
for g := range gch {
@@ -79,10 +76,10 @@ func TestMirrorSync_Authenticated(t *testing.T) {
}
// Start a continuous sync
- wch := syncer.SyncUpdates(context.TODO())
+ wch := syncer.SyncUpdates(t.Context())
// Update state
- _, err = syncClient.KV.Put(context.TODO(), "/syncpath/foo", "baz")
+ _, err = syncClient.KV.Put(t.Context(), "/syncpath/foo", "baz")
require.NoError(t, err)
// Wait for the updated state to sync
diff --git a/tests/integration/clientv3/mirror_test.go b/tests/integration/clientv3/mirror_test.go
index e3bc9a730093..4ed6151ab168 100644
--- a/tests/integration/clientv3/mirror_test.go
+++ b/tests/integration/clientv3/mirror_test.go
@@ -15,7 +15,6 @@
package clientv3test
import (
- "context"
"fmt"
"reflect"
"sync"
@@ -26,21 +25,21 @@ import (
"go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/client/v3/mirror"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestMirrorSync(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
c := clus.Client(0)
- _, err := c.KV.Put(context.TODO(), "foo", "bar")
+ _, err := c.KV.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
syncer := mirror.NewSyncer(c, "", 0)
- gch, ech := syncer.SyncBase(context.TODO())
+ gch, ech := syncer.SyncBase(t.Context())
wkvs := []*mvccpb.KeyValue{{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1}}
for g := range gch {
@@ -53,9 +52,9 @@ func TestMirrorSync(t *testing.T) {
t.Fatalf("unexpected error %v", e)
}
- wch := syncer.SyncUpdates(context.TODO())
+ wch := syncer.SyncUpdates(t.Context())
- _, err = c.KV.Put(context.TODO(), "foo", "bar")
+ _, err = c.KV.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
select {
@@ -70,13 +69,13 @@ func TestMirrorSync(t *testing.T) {
}
func TestMirrorSyncBase(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer cluster.Terminate(t)
cli := cluster.Client(0)
- ctx := context.TODO()
+ ctx := t.Context()
keyCh := make(chan string)
var wg sync.WaitGroup
diff --git a/tests/integration/clientv3/namespace_test.go b/tests/integration/clientv3/namespace_test.go
index 4ad7f60a28f9..3244a7037a4b 100644
--- a/tests/integration/clientv3/namespace_test.go
+++ b/tests/integration/clientv3/namespace_test.go
@@ -15,7 +15,6 @@
package clientv3test
import (
- "context"
"reflect"
"testing"
@@ -24,27 +23,27 @@ import (
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/namespace"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestNamespacePutGet(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
c := clus.Client(0)
nsKV := namespace.NewKV(c.KV, "foo/")
- _, err := nsKV.Put(context.TODO(), "abc", "bar")
+ _, err := nsKV.Put(t.Context(), "abc", "bar")
require.NoError(t, err)
- resp, err := nsKV.Get(context.TODO(), "abc")
+ resp, err := nsKV.Get(t.Context(), "abc")
require.NoError(t, err)
if string(resp.Kvs[0].Key) != "abc" {
t.Errorf("expected key=%q, got key=%q", "abc", resp.Kvs[0].Key)
}
- resp, err = c.Get(context.TODO(), "foo/abc")
+ resp, err = c.Get(t.Context(), "foo/abc")
require.NoError(t, err)
if string(resp.Kvs[0].Value) != "bar" {
t.Errorf("expected value=%q, got value=%q", "bar", resp.Kvs[0].Value)
@@ -52,25 +51,25 @@ func TestNamespacePutGet(t *testing.T) {
}
func TestNamespaceWatch(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
c := clus.Client(0)
nsKV := namespace.NewKV(c.KV, "foo/")
nsWatcher := namespace.NewWatcher(c.Watcher, "foo/")
- _, err := nsKV.Put(context.TODO(), "abc", "bar")
+ _, err := nsKV.Put(t.Context(), "abc", "bar")
require.NoError(t, err)
- nsWch := nsWatcher.Watch(context.TODO(), "abc", clientv3.WithRev(1))
+ nsWch := nsWatcher.Watch(t.Context(), "abc", clientv3.WithRev(1))
wkv := &mvccpb.KeyValue{Key: []byte("abc"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1}
if wr := <-nsWch; len(wr.Events) != 1 || !reflect.DeepEqual(wr.Events[0].Kv, wkv) {
t.Errorf("expected namespaced event %+v, got %+v", wkv, wr.Events[0].Kv)
}
- wch := c.Watch(context.TODO(), "foo/abc", clientv3.WithRev(1))
+ wch := c.Watch(t.Context(), "foo/abc", clientv3.WithRev(1))
wkv = &mvccpb.KeyValue{Key: []byte("foo/abc"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1}
if wr := <-wch; len(wr.Events) != 1 || !reflect.DeepEqual(wr.Events[0].Kv, wkv) {
t.Errorf("expected unnamespaced event %+v, got %+v", wkv, wr)
diff --git a/tests/integration/clientv3/naming/endpoints_test.go b/tests/integration/clientv3/naming/endpoints_test.go
index 3c93c6c7d191..f072cc89b511 100644
--- a/tests/integration/clientv3/naming/endpoints_test.go
+++ b/tests/integration/clientv3/naming/endpoints_test.go
@@ -23,20 +23,20 @@ import (
etcd "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestEndpointManager(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
em, err := endpoints.NewManager(clus.RandClient(), "foo")
if err != nil {
t.Fatal("failed to create EndpointManager", err)
}
- ctx, watchCancel := context.WithCancel(context.Background())
+ ctx, watchCancel := context.WithCancel(t.Context())
defer watchCancel()
w, err := em.NewWatchChannel(ctx)
if err != nil {
@@ -44,7 +44,7 @@ func TestEndpointManager(t *testing.T) {
}
e1 := endpoints.Endpoint{Addr: "127.0.0.1", Metadata: "metadata"}
- err = em.AddEndpoint(context.TODO(), "foo/a1", e1)
+ err = em.AddEndpoint(t.Context(), "foo/a1", e1)
if err != nil {
t.Fatal("failed to add foo", err)
}
@@ -61,14 +61,10 @@ func TestEndpointManager(t *testing.T) {
Endpoint: e1,
}
- if !reflect.DeepEqual(us[0], wu) {
- t.Fatalf("up = %#v, want %#v", us[0], wu)
- }
+ require.Truef(t, reflect.DeepEqual(us[0], wu), "up = %#v, want %#v", us[0], wu)
- err = em.DeleteEndpoint(context.TODO(), "foo/a1")
- if err != nil {
- t.Fatalf("failed to udpate %v", err)
- }
+ err = em.DeleteEndpoint(t.Context(), "foo/a1")
+ require.NoErrorf(t, err, "failed to udpate %v", err)
us = <-w
if us == nil {
@@ -80,18 +76,16 @@ func TestEndpointManager(t *testing.T) {
Key: "foo/a1",
}
- if !reflect.DeepEqual(us[0], wu) {
- t.Fatalf("up = %#v, want %#v", us[1], wu)
- }
+ require.Truef(t, reflect.DeepEqual(us[0], wu), "up = %#v, want %#v", us[0], wu)
}
// TestEndpointManagerAtomicity ensures the resolver will initialize
// correctly with multiple hosts and correctly receive multiple
// updates in a single revision.
func TestEndpointManagerAtomicity(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
c := clus.RandClient()
@@ -100,23 +94,21 @@ func TestEndpointManagerAtomicity(t *testing.T) {
t.Fatal("failed to create EndpointManager", err)
}
- err = em.Update(context.TODO(), []*endpoints.UpdateWithOpts{
+ err = em.Update(t.Context(), []*endpoints.UpdateWithOpts{
endpoints.NewAddUpdateOpts("foo/host", endpoints.Endpoint{Addr: "127.0.0.1:2000"}),
endpoints.NewAddUpdateOpts("foo/host2", endpoints.Endpoint{Addr: "127.0.0.1:2001"}),
})
require.NoError(t, err)
- ctx, watchCancel := context.WithCancel(context.Background())
+ ctx, watchCancel := context.WithCancel(t.Context())
defer watchCancel()
w, err := em.NewWatchChannel(ctx)
require.NoError(t, err)
updates := <-w
- if len(updates) != 2 {
- t.Fatalf("expected two updates, got %+v", updates)
- }
+ require.Lenf(t, updates, 2, "expected two updates, got %+v", updates)
- _, err = c.Txn(context.TODO()).Then(etcd.OpDelete("foo/host"), etcd.OpDelete("foo/host2")).Commit()
+ _, err = c.Txn(t.Context()).Then(etcd.OpDelete("foo/host"), etcd.OpDelete("foo/host2")).Commit()
require.NoError(t, err)
updates = <-w
@@ -126,9 +118,9 @@ func TestEndpointManagerAtomicity(t *testing.T) {
}
func TestEndpointManagerCRUD(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
em, err := endpoints.NewManager(clus.RandClient(), "foo")
@@ -139,48 +131,38 @@ func TestEndpointManagerCRUD(t *testing.T) {
// Add
k1 := "foo/a1"
e1 := endpoints.Endpoint{Addr: "127.0.0.1", Metadata: "metadata1"}
- err = em.AddEndpoint(context.TODO(), k1, e1)
+ err = em.AddEndpoint(t.Context(), k1, e1)
if err != nil {
t.Fatal("failed to add", k1, err)
}
k2 := "foo/a2"
e2 := endpoints.Endpoint{Addr: "127.0.0.2", Metadata: "metadata2"}
- err = em.AddEndpoint(context.TODO(), k2, e2)
+ err = em.AddEndpoint(t.Context(), k2, e2)
if err != nil {
t.Fatal("failed to add", k2, err)
}
- eps, err := em.List(context.TODO())
+ eps, err := em.List(t.Context())
if err != nil {
t.Fatal("failed to list foo")
}
- if len(eps) != 2 {
- t.Fatalf("unexpected the number of endpoints: %d", len(eps))
- }
- if !reflect.DeepEqual(eps[k1], e1) {
- t.Fatalf("unexpected endpoints: %s", k1)
- }
- if !reflect.DeepEqual(eps[k2], e2) {
- t.Fatalf("unexpected endpoints: %s", k2)
- }
+ require.Lenf(t, eps, 2, "unexpected the number of endpoints: %d", len(eps))
+ require.Truef(t, reflect.DeepEqual(eps[k1], e1), "unexpected endpoints: %s", k1)
+ require.Truef(t, reflect.DeepEqual(eps[k2], e2), "unexpected endpoints: %s", k2)
// Delete
- err = em.DeleteEndpoint(context.TODO(), k1)
+ err = em.DeleteEndpoint(t.Context(), k1)
if err != nil {
t.Fatal("failed to delete", k2, err)
}
- eps, err = em.List(context.TODO())
+ eps, err = em.List(t.Context())
if err != nil {
t.Fatal("failed to list foo")
}
- if len(eps) != 1 {
- t.Fatalf("unexpected the number of endpoints: %d", len(eps))
- }
- if !reflect.DeepEqual(eps[k2], e2) {
- t.Fatalf("unexpected endpoints: %s", k2)
- }
+ require.Lenf(t, eps, 1, "unexpected the number of endpoints: %d", len(eps))
+ require.Truef(t, reflect.DeepEqual(eps[k2], e2), "unexpected endpoints: %s", k2)
// Update
k3 := "foo/a3"
@@ -189,19 +171,15 @@ func TestEndpointManagerCRUD(t *testing.T) {
{Update: endpoints.Update{Op: endpoints.Add, Key: k3, Endpoint: e3}},
{Update: endpoints.Update{Op: endpoints.Delete, Key: k2}},
}
- err = em.Update(context.TODO(), updates)
+ err = em.Update(t.Context(), updates)
if err != nil {
t.Fatal("failed to update", err)
}
- eps, err = em.List(context.TODO())
+ eps, err = em.List(t.Context())
if err != nil {
t.Fatal("failed to list foo")
}
- if len(eps) != 1 {
- t.Fatalf("unexpected the number of endpoints: %d", len(eps))
- }
- if !reflect.DeepEqual(eps[k3], e3) {
- t.Fatalf("unexpected endpoints: %s", k3)
- }
+ require.Lenf(t, eps, 1, "unexpected the number of endpoints: %d", len(eps))
+ require.Truef(t, reflect.DeepEqual(eps[k3], e3), "unexpected endpoints: %s", k3)
}
diff --git a/tests/integration/clientv3/naming/main_test.go b/tests/integration/clientv3/naming/main_test.go
index 92a58a54c7e4..bbebf1c918e2 100644
--- a/tests/integration/clientv3/naming/main_test.go
+++ b/tests/integration/clientv3/naming/main_test.go
@@ -1,6 +1,16 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2025 The etcd Authors
+//
+// 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 naming_test
diff --git a/tests/integration/clientv3/naming/resolver_test.go b/tests/integration/clientv3/naming/resolver_test.go
index f39dbb2c2341..8cd5c674859b 100644
--- a/tests/integration/clientv3/naming/resolver_test.go
+++ b/tests/integration/clientv3/naming/resolver_test.go
@@ -16,7 +16,6 @@ package naming_test
import (
"bytes"
- "context"
"fmt"
"strconv"
"testing"
@@ -30,7 +29,7 @@ import (
"go.etcd.io/etcd/client/v3/naming/endpoints"
"go.etcd.io/etcd/client/v3/naming/resolver"
"go.etcd.io/etcd/pkg/v3/grpctesting"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
@@ -49,7 +48,7 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
defer s2.Stop()
// Create new cluster with endpoint manager with two endpoints
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
em, err := endpoints.NewManager(clus.Client(0), "foo")
@@ -60,12 +59,12 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
e1 := endpoints.Endpoint{Addr: s1.Addr()}
e2 := endpoints.Endpoint{Addr: s2.Addr()}
- err = em.AddEndpoint(context.TODO(), "foo/e1", e1)
+ err = em.AddEndpoint(t.Context(), "foo/e1", e1)
if err != nil {
t.Fatal("failed to add foo", err)
}
- err = em.AddEndpoint(context.TODO(), "foo/e2", e2)
+ err = em.AddEndpoint(t.Context(), "foo/e2", e2)
if err != nil {
t.Fatal("failed to add foo", err)
}
@@ -76,7 +75,7 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
}
// Create connection with provided lb policy
- conn, err := grpc.Dial("etcd:///foo", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(b),
+ conn, err := grpc.NewClient("etcd:///foo", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(b),
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingPolicy":"%s"}`, lbPolicy)))
if err != nil {
t.Fatal("failed to connect to foo", err)
@@ -85,7 +84,7 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
// Send an initial request that should go to e1
c := testpb.NewTestServiceClient(conn)
- resp, err := c.UnaryCall(context.TODO(), &testpb.SimpleRequest{}, grpc.WaitForReady(true))
+ resp, err := c.UnaryCall(t.Context(), &testpb.SimpleRequest{}, grpc.WaitForReady(true))
if err != nil {
t.Fatal("failed to invoke rpc to foo (e1)", err)
}
@@ -97,33 +96,27 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
lastResponse := []byte{'1'}
totalRequests := 3500
for i := 1; i < totalRequests; i++ {
- resp, err := c.UnaryCall(context.TODO(), &testpb.SimpleRequest{}, grpc.WaitForReady(true))
+ resp, err := c.UnaryCall(t.Context(), &testpb.SimpleRequest{}, grpc.WaitForReady(true))
if err != nil {
t.Fatal("failed to invoke rpc to foo", err)
}
t.Logf("Response: %v", string(resp.GetPayload().GetBody()))
- if resp.GetPayload() == nil {
- t.Fatalf("unexpected response from foo: %s", resp.GetPayload().GetBody())
- }
+ require.NotNilf(t, resp.GetPayload(), "unexpected response from foo: %s", resp.GetPayload().GetBody())
lastResponse = resp.GetPayload().GetBody()
}
// If the load balancing policy is pick first then return payload should equal number of requests
t.Logf("Last response: %v", string(lastResponse))
if lbPolicy == "pick_first" {
- if string(lastResponse) != "3500" {
- t.Fatalf("unexpected total responses from foo: %s", lastResponse)
- }
+ require.Equalf(t, "3500", string(lastResponse), "unexpected total responses from foo: %s", lastResponse)
}
// If the load balancing policy is round robin we should see roughly half total requests served by each server
if lbPolicy == "round_robin" {
responses, err := strconv.Atoi(string(lastResponse))
- if err != nil {
- t.Fatalf("couldn't convert to int: %s", lastResponse)
- }
+ require.NoErrorf(t, err, "couldn't convert to int: %s", lastResponse)
// Allow 25% tolerance as round robin is not perfect and we don't want the test to flake
expected := float64(totalRequests) * 0.5
@@ -133,7 +126,7 @@ func testEtcdGRPCResolver(t *testing.T, lbPolicy string) {
// TestEtcdGrpcResolverPickFirst mimics scenarios described in grpc_naming.md doc.
func TestEtcdGrpcResolverPickFirst(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// Pick first is the default load balancer policy for grpc-go
testEtcdGRPCResolver(t, "pick_first")
@@ -141,14 +134,14 @@ func TestEtcdGrpcResolverPickFirst(t *testing.T) {
// TestEtcdGrpcResolverRoundRobin mimics scenarios described in grpc_naming.md doc.
func TestEtcdGrpcResolverRoundRobin(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// Round robin is a common alternative for more production oriented scenarios
testEtcdGRPCResolver(t, "round_robin")
}
func TestEtcdEndpointManager(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
s1PayloadBody := []byte{'1'}
s1 := grpctesting.NewDummyStubServer(s1PayloadBody)
@@ -162,7 +155,7 @@ func TestEtcdEndpointManager(t *testing.T) {
require.NoError(t, err)
defer s2.Stop()
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// Check if any endpoint with the same prefix "foo" will not break the logic with multiple endpoints
@@ -174,12 +167,12 @@ func TestEtcdEndpointManager(t *testing.T) {
e1 := endpoints.Endpoint{Addr: s1.Addr()}
e2 := endpoints.Endpoint{Addr: s2.Addr()}
- em.AddEndpoint(context.Background(), "foo/e1", e1)
- emOther.AddEndpoint(context.Background(), "foo_other/e2", e2)
+ em.AddEndpoint(t.Context(), "foo/e1", e1)
+ emOther.AddEndpoint(t.Context(), "foo_other/e2", e2)
- epts, err := em.List(context.Background())
+ epts, err := em.List(t.Context())
require.NoError(t, err)
- eptsOther, err := emOther.List(context.Background())
+ eptsOther, err := emOther.List(t.Context())
require.NoError(t, err)
assert.Len(t, epts, 1)
assert.Len(t, eptsOther, 1)
diff --git a/tests/integration/clientv3/ordering_kv_test.go b/tests/integration/clientv3/ordering_kv_test.go
index 2ba6aaa88016..4d81a6ad0296 100644
--- a/tests/integration/clientv3/ordering_kv_test.go
+++ b/tests/integration/clientv3/ordering_kv_test.go
@@ -15,7 +15,6 @@
package clientv3test
import (
- "context"
"errors"
"testing"
"time"
@@ -25,14 +24,14 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/ordering"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestDetectKvOrderViolation(t *testing.T) {
errOrderViolation := errors.New("DetectedOrderViolation")
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
cfg := clientv3.Config{
@@ -42,10 +41,10 @@ func TestDetectKvOrderViolation(t *testing.T) {
clus.Members[2].GRPCURL,
},
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer func() { assert.NoError(t, cli.Close()) }()
- ctx := context.TODO()
+ ctx := t.Context()
_, err = clus.Client(0).Put(ctx, "foo", "bar")
require.NoError(t, err)
@@ -90,8 +89,8 @@ func TestDetectKvOrderViolation(t *testing.T) {
func TestDetectTxnOrderViolation(t *testing.T) {
errOrderViolation := errors.New("DetectedOrderViolation")
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
cfg := clientv3.Config{
@@ -101,10 +100,10 @@ func TestDetectTxnOrderViolation(t *testing.T) {
clus.Members[2].GRPCURL,
},
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer func() { assert.NoError(t, cli.Close()) }()
- ctx := context.TODO()
+ ctx := t.Context()
_, err = clus.Client(0).Put(ctx, "foo", "bar")
require.NoError(t, err)
diff --git a/tests/integration/clientv3/ordering_util_test.go b/tests/integration/clientv3/ordering_util_test.go
index bb46989e4798..cd4d072ac081 100644
--- a/tests/integration/clientv3/ordering_util_test.go
+++ b/tests/integration/clientv3/ordering_util_test.go
@@ -15,7 +15,6 @@
package clientv3test
import (
- "context"
"errors"
"testing"
"time"
@@ -24,15 +23,15 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/ordering"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// TestEndpointSwitchResolvesViolation ensures
// - ErrNoGreaterRev error is returned from partitioned member when it has stale revision
// - no more error after partition recovers
func TestEndpointSwitchResolvesViolation(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
eps := []string{
clus.Members[0].GRPCURL,
@@ -40,11 +39,11 @@ func TestEndpointSwitchResolvesViolation(t *testing.T) {
clus.Members[2].GRPCURL,
}
cfg := clientv3.Config{Endpoints: []string{clus.Members[0].GRPCURL}}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer cli.Close()
- ctx := context.TODO()
+ ctx := t.Context()
_, err = clus.Client(0).Put(ctx, "foo", "bar")
require.NoError(t, err)
@@ -88,8 +87,8 @@ func TestEndpointSwitchResolvesViolation(t *testing.T) {
// TestUnresolvableOrderViolation ensures ErrNoGreaterRev error is returned when available members only have stale revisions
func TestUnresolvableOrderViolation(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 5, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 5, UseBridge: true})
defer clus.Terminate(t)
cfg := clientv3.Config{
Endpoints: []string{
@@ -100,11 +99,11 @@ func TestUnresolvableOrderViolation(t *testing.T) {
clus.Members[4].GRPCURL,
},
}
- cli, err := integration2.NewClient(t, cfg)
+ cli, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer cli.Close()
eps := cli.Endpoints()
- ctx := context.TODO()
+ ctx := t.Context()
cli.SetEndpoints(clus.Members[0].GRPCURL)
time.Sleep(1 * time.Second)
@@ -138,7 +137,5 @@ func TestUnresolvableOrderViolation(t *testing.T) {
time.Sleep(1 * time.Second) // give enough time for operation
_, err = OrderingKv.Get(ctx, "foo", clientv3.WithSerializable())
- if !errors.Is(err, ordering.ErrNoGreaterRev) {
- t.Fatalf("expected %v, got %v", ordering.ErrNoGreaterRev, err)
- }
+ require.ErrorIsf(t, err, ordering.ErrNoGreaterRev, "expected %v, got %v", ordering.ErrNoGreaterRev, err)
}
diff --git a/tests/integration/clientv3/snapshot/v3_snapshot_test.go b/tests/integration/clientv3/snapshot/v3_snapshot_test.go
index c39b092e1cf6..3148f680b4ee 100644
--- a/tests/integration/clientv3/snapshot/v3_snapshot_test.go
+++ b/tests/integration/clientv3/snapshot/v3_snapshot_test.go
@@ -32,7 +32,7 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/snapshot"
"go.etcd.io/etcd/server/v3/embed"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// TestSaveSnapshotFilePermissions ensures that the snapshot is saved with
@@ -44,14 +44,10 @@ func TestSaveSnapshotFilePermissions(t *testing.T) {
defer os.RemoveAll(dbPath)
dbInfo, err := os.Stat(dbPath)
- if err != nil {
- t.Fatalf("failed to get test snapshot file status: %v", err)
- }
+ require.NoErrorf(t, err, "failed to get test snapshot file status: %v", err)
actualFileMode := dbInfo.Mode()
- if expectedFileMode != actualFileMode {
- t.Fatalf("expected test snapshot file mode %s, got %s:", expectedFileMode, actualFileMode)
- }
+ require.Equalf(t, expectedFileMode, actualFileMode, "expected test snapshot file mode %s, got %s:", expectedFileMode, actualFileMode)
}
// TestSaveSnapshotVersion ensures that the snapshot returns proper storage version.
@@ -67,9 +63,7 @@ func TestSaveSnapshotVersion(t *testing.T) {
ver, dbPath := createSnapshotFile(t, cfg, kvs)
defer os.RemoveAll(dbPath)
- if ver != "3.6.0" {
- t.Fatalf("expected snapshot version %s, got %s:", "3.6.0", ver)
- }
+ require.Equalf(t, "3.7.0", ver, "expected snapshot version %s, got %s:", "3.7.0", ver)
}
type kv struct {
@@ -80,7 +74,7 @@ func newEmbedConfig(t *testing.T) *embed.Config {
clusterN := 1
urls := newEmbedURLs(clusterN * 2)
cURLs, pURLs := urls[:clusterN], urls[clusterN:]
- cfg := integration2.NewEmbedConfig(t, "default")
+ cfg := integration.NewEmbedConfig(t, "default")
cfg.ClusterState = "new"
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = cURLs, cURLs
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = pURLs, pURLs
@@ -105,18 +99,18 @@ func createSnapshotFile(t *testing.T, cfg *embed.Config, kvs []kv) (version stri
}
ccfg := clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}}
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
for i := range kvs {
- ctx, cancel := context.WithTimeout(context.Background(), testutil.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), testutil.RequestTimeout)
_, err = cli.Put(ctx, kvs[i].k, kvs[i].v)
cancel()
require.NoError(t, err)
}
dbPath = filepath.Join(t.TempDir(), fmt.Sprintf("snapshot%d.db", time.Now().Nanosecond()))
- version, err = snapshot.SaveWithVersion(context.Background(), zaptest.NewLogger(t), ccfg, dbPath)
+ version, err = snapshot.SaveWithVersion(t.Context(), zaptest.NewLogger(t), ccfg, dbPath)
require.NoError(t, err)
return version, dbPath
}
diff --git a/tests/integration/clientv3/txn_test.go b/tests/integration/clientv3/txn_test.go
index 00266e49c586..985136ba8a3f 100644
--- a/tests/integration/clientv3/txn_test.go
+++ b/tests/integration/clientv3/txn_test.go
@@ -26,17 +26,17 @@ import (
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/embed"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestTxnError(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.RandClient()
- ctx := context.TODO()
+ ctx := t.Context()
_, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar1"), clientv3.OpPut("foo", "bar2")).Commit()
if !errors.Is(err, rpctypes.ErrDuplicateKey) {
@@ -54,9 +54,9 @@ func TestTxnError(t *testing.T) {
}
func TestTxnWriteFail(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
kv := clus.Client(0)
@@ -65,7 +65,7 @@ func TestTxnWriteFail(t *testing.T) {
txnc, getc := make(chan struct{}), make(chan struct{})
go func() {
- ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
defer cancel()
resp, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit()
if err == nil {
@@ -82,7 +82,7 @@ func TestTxnWriteFail(t *testing.T) {
case <-txnc:
}
// and ensure the put didn't take
- gresp, gerr := clus.Client(1).Get(context.TODO(), "foo")
+ gresp, gerr := clus.Client(1).Get(t.Context(), "foo")
if gerr != nil {
t.Error(gerr)
}
@@ -104,9 +104,9 @@ func TestTxnWriteFail(t *testing.T) {
func TestTxnReadRetry(t *testing.T) {
t.Skipf("skipping txn read retry test: re-enable after we do retry on txn read request")
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
kv := clus.Client(0)
@@ -123,7 +123,7 @@ func TestTxnReadRetry(t *testing.T) {
donec := make(chan struct{}, 1)
go func() {
- _, err := kv.Txn(context.TODO()).Then(thenOps[i]...).Commit()
+ _, err := kv.Txn(t.Context()).Then(thenOps[i]...).Commit()
if err != nil {
t.Errorf("expected response, got error %v", err)
}
@@ -143,13 +143,13 @@ func TestTxnReadRetry(t *testing.T) {
}
func TestTxnSuccess(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
kv := clus.Client(0)
- ctx := context.TODO()
+ ctx := t.Context()
_, err := kv.Txn(ctx).Then(clientv3.OpPut("foo", "bar")).Commit()
require.NoError(t, err)
@@ -162,17 +162,17 @@ func TestTxnSuccess(t *testing.T) {
}
func TestTxnCompareRange(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kv := clus.Client(0)
- fooResp, err := kv.Put(context.TODO(), "foo/", "bar")
+ fooResp, err := kv.Put(t.Context(), "foo/", "bar")
require.NoError(t, err)
- _, err = kv.Put(context.TODO(), "foo/a", "baz")
+ _, err = kv.Put(t.Context(), "foo/a", "baz")
require.NoError(t, err)
- tresp, terr := kv.Txn(context.TODO()).If(
+ tresp, terr := kv.Txn(t.Context()).If(
clientv3.Compare(
clientv3.CreateRevision("foo/"), "=", fooResp.Header.Revision).
WithPrefix(),
@@ -184,14 +184,14 @@ func TestTxnCompareRange(t *testing.T) {
}
func TestTxnNested(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
kv := clus.Client(0)
- tresp, err := kv.Txn(context.TODO()).
+ tresp, err := kv.Txn(t.Context()).
If(clientv3.Compare(clientv3.Version("foo"), "=", 0)).
Then(
clientv3.OpPut("foo", "bar"),
@@ -203,12 +203,12 @@ func TestTxnNested(t *testing.T) {
}
// check txn writes were applied
- resp, err := kv.Get(context.TODO(), "foo")
+ resp, err := kv.Get(t.Context(), "foo")
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "bar" {
t.Errorf("unexpected Get response %+v", resp)
}
- resp, err = kv.Get(context.TODO(), "abc")
+ resp, err = kv.Get(t.Context(), "abc")
require.NoError(t, err)
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "123" {
t.Errorf("unexpected Get response %+v", resp)
diff --git a/tests/integration/clientv3/user_test.go b/tests/integration/clientv3/user_test.go
index 201740fefcfa..696fe955bb30 100644
--- a/tests/integration/clientv3/user_test.go
+++ b/tests/integration/clientv3/user_test.go
@@ -21,44 +21,37 @@ import (
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestUserError(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
authapi := clus.RandClient()
- _, err := authapi.UserAdd(context.TODO(), "foo", "bar")
+ _, err := authapi.UserAdd(t.Context(), "foo", "bar")
require.NoError(t, err)
- _, err = authapi.UserAdd(context.TODO(), "foo", "bar")
- if !errors.Is(err, rpctypes.ErrUserAlreadyExist) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrUserAlreadyExist, err)
- }
+ _, err = authapi.UserAdd(t.Context(), "foo", "bar")
+ require.ErrorIsf(t, err, rpctypes.ErrUserAlreadyExist, "expected %v, got %v", rpctypes.ErrUserAlreadyExist, err)
- _, err = authapi.UserDelete(context.TODO(), "not-exist-user")
- if !errors.Is(err, rpctypes.ErrUserNotFound) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrUserNotFound, err)
- }
+ _, err = authapi.UserDelete(t.Context(), "not-exist-user")
+ require.ErrorIsf(t, err, rpctypes.ErrUserNotFound, "expected %v, got %v", rpctypes.ErrUserNotFound, err)
- _, err = authapi.UserGrantRole(context.TODO(), "foo", "test-role-does-not-exist")
- if !errors.Is(err, rpctypes.ErrRoleNotFound) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrRoleNotFound, err)
- }
+ _, err = authapi.UserGrantRole(t.Context(), "foo", "test-role-does-not-exist")
+ require.ErrorIsf(t, err, rpctypes.ErrRoleNotFound, "expected %v, got %v", rpctypes.ErrRoleNotFound, err)
}
func TestAddUserAfterDelete(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
authapi := clus.RandClient()
@@ -66,100 +59,95 @@ func TestAddUserAfterDelete(t *testing.T) {
cfg := clientv3.Config{
Endpoints: authapi.Endpoints(),
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
cfg.Username, cfg.Password = "root", "123"
- authed, err := integration2.NewClient(t, cfg)
+ authed, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer authed.Close()
// add user
- _, err = authed.UserAdd(context.TODO(), "foo", "bar")
+ _, err = authed.UserAdd(t.Context(), "foo", "bar")
require.NoError(t, err)
- _, err = authapi.Authenticate(context.TODO(), "foo", "bar")
+ _, err = authapi.Authenticate(t.Context(), "foo", "bar")
require.NoError(t, err)
// delete user
- _, err = authed.UserDelete(context.TODO(), "foo")
+ _, err = authed.UserDelete(t.Context(), "foo")
require.NoError(t, err)
- if _, err = authed.Authenticate(context.TODO(), "foo", "bar"); err == nil {
+ if _, err = authed.Authenticate(t.Context(), "foo", "bar"); err == nil {
t.Errorf("expect Authenticate error for old password")
}
// add user back
- _, err = authed.UserAdd(context.TODO(), "foo", "bar")
+ _, err = authed.UserAdd(t.Context(), "foo", "bar")
require.NoError(t, err)
- _, err = authed.Authenticate(context.TODO(), "foo", "bar")
+ _, err = authed.Authenticate(t.Context(), "foo", "bar")
require.NoError(t, err)
// change password
- _, err = authed.UserChangePassword(context.TODO(), "foo", "bar2")
+ _, err = authed.UserChangePassword(t.Context(), "foo", "bar2")
require.NoError(t, err)
- _, err = authed.UserChangePassword(context.TODO(), "foo", "bar1")
+ _, err = authed.UserChangePassword(t.Context(), "foo", "bar1")
require.NoError(t, err)
- if _, err = authed.Authenticate(context.TODO(), "foo", "bar"); err == nil {
+ if _, err = authed.Authenticate(t.Context(), "foo", "bar"); err == nil {
t.Errorf("expect Authenticate error for old password")
}
- if _, err = authed.Authenticate(context.TODO(), "foo", "bar2"); err == nil {
+ if _, err = authed.Authenticate(t.Context(), "foo", "bar2"); err == nil {
t.Errorf("expect Authenticate error for old password")
}
- _, err = authed.Authenticate(context.TODO(), "foo", "bar1")
+ _, err = authed.Authenticate(t.Context(), "foo", "bar1")
require.NoError(t, err)
}
func TestUserErrorAuth(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
authapi := clus.RandClient()
authSetupRoot(t, authapi.Auth)
// unauthenticated client
- if _, err := authapi.UserAdd(context.TODO(), "foo", "bar"); !errors.Is(err, rpctypes.ErrUserEmpty) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrUserEmpty, err)
- }
+ _, err := authapi.UserAdd(t.Context(), "foo", "bar")
+ require.ErrorIsf(t, err, rpctypes.ErrUserEmpty, "expected %v, got %v", rpctypes.ErrUserEmpty, err)
// wrong id or password
cfg := clientv3.Config{
Endpoints: authapi.Endpoints(),
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
}
cfg.Username, cfg.Password = "wrong-id", "123"
- if _, err := integration2.NewClient(t, cfg); !errors.Is(err, rpctypes.ErrAuthFailed) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrAuthFailed, err)
- }
+ _, err = integration.NewClient(t, cfg)
+ require.ErrorIsf(t, err, rpctypes.ErrAuthFailed, "expected %v, got %v", rpctypes.ErrAuthFailed, err)
cfg.Username, cfg.Password = "root", "wrong-pass"
- if _, err := integration2.NewClient(t, cfg); !errors.Is(err, rpctypes.ErrAuthFailed) {
- t.Fatalf("expected %v, got %v", rpctypes.ErrAuthFailed, err)
- }
+ _, err = integration.NewClient(t, cfg)
+ require.ErrorIsf(t, err, rpctypes.ErrAuthFailed, "expected %v, got %v", rpctypes.ErrAuthFailed, err)
cfg.Username, cfg.Password = "root", "123"
- authed, err := integration2.NewClient(t, cfg)
+ authed, err := integration.NewClient(t, cfg)
require.NoError(t, err)
defer authed.Close()
- _, err = authed.UserList(context.TODO())
+ _, err = authed.UserList(t.Context())
require.NoError(t, err)
}
func authSetupRoot(t *testing.T, auth clientv3.Auth) {
- _, err := auth.UserAdd(context.TODO(), "root", "123")
+ _, err := auth.UserAdd(t.Context(), "root", "123")
require.NoError(t, err)
- _, err = auth.RoleAdd(context.TODO(), "root")
+ _, err = auth.RoleAdd(t.Context(), "root")
require.NoError(t, err)
- _, err = auth.UserGrantRole(context.TODO(), "root", "root")
+ _, err = auth.UserGrantRole(t.Context(), "root", "root")
require.NoError(t, err)
- _, err = auth.AuthEnable(context.TODO())
+ _, err = auth.AuthEnable(t.Context())
require.NoError(t, err)
}
// TestGetTokenWithoutAuth is when Client can connect to etcd even if they
// supply credentials and the server is in AuthDisable mode.
func TestGetTokenWithoutAuth(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 2})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 2})
defer clus.Terminate(t)
authapi := clus.RandClient()
@@ -168,7 +156,7 @@ func TestGetTokenWithoutAuth(t *testing.T) {
var client *clientv3.Client
// make sure "auth" was disabled
- _, err = authapi.AuthDisable(context.TODO())
+ _, err = authapi.AuthDisable(t.Context())
require.NoError(t, err)
// "Username" and "Password" must be used
@@ -179,7 +167,7 @@ func TestGetTokenWithoutAuth(t *testing.T) {
Password: "123",
}
- client, err = integration2.NewClient(t, cfg)
+ client, err = integration.NewClient(t, cfg)
if err == nil {
defer client.Close()
}
diff --git a/tests/integration/clientv3/util.go b/tests/integration/clientv3/util.go
index 0e84115704c8..e2fd11d25f3b 100644
--- a/tests/integration/clientv3/util.go
+++ b/tests/integration/clientv3/util.go
@@ -26,14 +26,14 @@ import (
"google.golang.org/grpc/status"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// MustWaitPinReady waits up to 3-second until connection is up (pin endpoint).
// Fatal on time-out.
func MustWaitPinReady(t *testing.T, cli *clientv3.Client) {
// TODO: decrease timeout after balancer rewrite!!!
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
_, err := cli.Get(ctx, "foo")
cancel()
if err != nil {
@@ -108,8 +108,8 @@ func IsUnavailable(err error) bool {
// populateDataIntoCluster populates the key-value pairs into cluster and the
// key will be named by testing.T.Name()-index.
-func populateDataIntoCluster(t *testing.T, cluster *integration2.Cluster, numKeys int, valueSize int) {
- ctx := context.Background()
+func populateDataIntoCluster(t *testing.T, cluster *integration.Cluster, numKeys int, valueSize int) {
+ ctx := t.Context()
for i := 0; i < numKeys; i++ {
_, err := cluster.RandClient().Put(ctx,
diff --git a/tests/integration/v3_watch_restore_test.go b/tests/integration/clientv3/watch/v3_watch_restore_test.go
similarity index 96%
rename from tests/integration/v3_watch_restore_test.go
rename to tests/integration/clientv3/watch/v3_watch_restore_test.go
index 9d98cb8dcef4..618c6b0ab8ac 100644
--- a/tests/integration/v3_watch_restore_test.go
+++ b/tests/integration/clientv3/watch/v3_watch_restore_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package integration
+package watch
import (
"context"
@@ -64,7 +64,7 @@ func TestV3WatchRestoreSnapshotUnsync(t *testing.T) {
defer clus.Terminate(t)
// spawn a watcher before shutdown, and put it in synced watcher
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, errW := integration.ToGRPC(clus.Client(0)).Watch.Watch(ctx)
require.NoError(t, errW)
@@ -92,7 +92,7 @@ func TestV3WatchRestoreSnapshotUnsync(t *testing.T) {
// to trigger snapshot from the leader to the stopped follower
for i := 0; i < 15; i++ {
- _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
if err != nil {
t.Errorf("#%d: couldn't put key (%v)", i, err)
}
@@ -177,7 +177,7 @@ func TestV3WatchRestoreSnapshotUnsync(t *testing.T) {
}
func expectMemberLog(t *testing.T, m *integration.Member, timeout time.Duration, s string, count int) {
- ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+ ctx, cancel := context.WithTimeout(t.Context(), timeout)
defer cancel()
lines, err := m.LogObserver.Expect(ctx, s, count)
diff --git a/tests/integration/v3_watch_test.go b/tests/integration/clientv3/watch/v3_watch_test.go
similarity index 92%
rename from tests/integration/v3_watch_test.go
rename to tests/integration/clientv3/watch/v3_watch_test.go
index 21140da097b3..f8ea8f013be1 100644
--- a/tests/integration/v3_watch_test.go
+++ b/tests/integration/clientv3/watch/v3_watch_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package integration
+package watch
import (
"bytes"
@@ -64,7 +64,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
},
@@ -98,7 +98,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("fooLong"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
},
@@ -133,7 +133,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("fooLong"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
},
@@ -155,7 +155,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
},
@@ -165,7 +165,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 3, Version: 2},
},
},
@@ -175,7 +175,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 4, Version: 3},
},
},
@@ -198,7 +198,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
},
@@ -208,7 +208,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 3, Version: 2},
},
},
@@ -218,7 +218,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
Created: false,
Events: []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 4, Version: 3},
},
},
@@ -233,7 +233,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
defer clus.Terminate(t)
wAPI := integration.ToGRPC(clus.RandClient()).Watch
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, err := wAPI.Watch(ctx)
if err != nil {
@@ -268,7 +268,7 @@ func TestV3WatchFromCurrentRevision(t *testing.T) {
for _, k := range tt.putKeys {
kvc := integration.ToGRPC(clus.RandClient()).KV
req := &pb.PutRequest{Key: []byte(k), Value: []byte("bar")}
- if _, err := kvc.Put(context.TODO(), req); err != nil {
+ if _, err := kvc.Put(t.Context(), req); err != nil {
t.Errorf("#%d: couldn't put key (%v)", i, err)
}
}
@@ -320,7 +320,7 @@ func TestV3WatchFutureRevision(t *testing.T) {
defer clus.Terminate(t)
wAPI := integration.ToGRPC(clus.RandClient()).Watch
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, err := wAPI.Watch(ctx)
if err != nil {
@@ -349,7 +349,7 @@ func TestV3WatchFutureRevision(t *testing.T) {
kvc := integration.ToGRPC(clus.RandClient()).KV
for {
req := &pb.PutRequest{Key: wkey, Value: []byte("bar")}
- resp, rerr := kvc.Put(context.TODO(), req)
+ resp, rerr := kvc.Put(t.Context(), req)
if rerr != nil {
t.Fatalf("couldn't put key (%v)", rerr)
}
@@ -382,7 +382,7 @@ func TestV3WatchWrongRange(t *testing.T) {
defer clus.Terminate(t)
wAPI := integration.ToGRPC(clus.RandClient()).Watch
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, err := wAPI.Watch(ctx)
if err != nil {
@@ -436,7 +436,7 @@ func testV3WatchCancel(t *testing.T, startRev int64) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, errW := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if errW != nil {
@@ -478,7 +478,7 @@ func testV3WatchCancel(t *testing.T, startRev int64) {
}
kvc := integration.ToGRPC(clus.RandClient()).KV
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
t.Errorf("couldn't put key (%v)", err)
}
@@ -496,7 +496,7 @@ func TestV3WatchCurrentPutOverlap(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, wErr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if wErr != nil {
@@ -513,7 +513,7 @@ func TestV3WatchCurrentPutOverlap(t *testing.T) {
defer wg.Done()
kvc := integration.ToGRPC(clus.RandClient()).KV
req := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
- if _, err := kvc.Put(context.TODO(), req); err != nil {
+ if _, err := kvc.Put(t.Context(), req); err != nil {
t.Errorf("couldn't put key (%v)", err)
}
}()
@@ -582,7 +582,7 @@ func TestV3WatchEmptyKey(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
ws, werr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
@@ -599,7 +599,7 @@ func TestV3WatchEmptyKey(t *testing.T) {
// put a key with empty value
kvc := integration.ToGRPC(clus.RandClient()).KV
preq := &pb.PutRequest{Key: []byte("foo")}
- _, err = kvc.Put(context.TODO(), preq)
+ _, err = kvc.Put(t.Context(), preq)
require.NoError(t, err)
// check received PUT
@@ -607,7 +607,7 @@ func TestV3WatchEmptyKey(t *testing.T) {
require.NoError(t, rerr)
wevs := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
}
@@ -636,7 +636,7 @@ func testV3WatchMultipleWatchers(t *testing.T, startRev int64) {
kvc := integration.ToGRPC(clus.RandClient()).KV
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, errW := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if errW != nil {
@@ -676,7 +676,7 @@ func testV3WatchMultipleWatchers(t *testing.T, startRev int64) {
ids[wresp.WatchId] = struct{}{}
}
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
@@ -704,7 +704,7 @@ func testV3WatchMultipleWatchers(t *testing.T, startRev int64) {
}
// now put one key that has only one matching watcher
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("fo"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("fo"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
wresp, err := wStream.Recv()
@@ -740,7 +740,7 @@ func testV3WatchMultipleEventsTxn(t *testing.T, startRev int64) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, wErr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if wErr != nil {
@@ -771,7 +771,7 @@ func testV3WatchMultipleEventsTxn(t *testing.T, startRev int64) {
txn.Success = append(txn.Success, ru)
}
- tresp, err := kvc.Txn(context.Background(), &txn)
+ tresp, err := kvc.Txn(t.Context(), &txn)
if err != nil {
t.Fatalf("kvc.Txn error: %v", err)
}
@@ -791,15 +791,15 @@ func testV3WatchMultipleEventsTxn(t *testing.T, startRev int64) {
wevents := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo0"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo1"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo2"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
}
@@ -829,14 +829,14 @@ func TestV3WatchMultipleEventsPutUnsynced(t *testing.T) {
kvc := integration.ToGRPC(clus.RandClient()).KV
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo0"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo0"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo1"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo1"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, wErr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if wErr != nil {
@@ -852,28 +852,28 @@ func TestV3WatchMultipleEventsPutUnsynced(t *testing.T) {
t.Fatalf("wStream.Send error: %v", err)
}
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo0"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo0"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo1"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo1"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
allWevents := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo0"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo1"), Value: []byte("bar"), CreateRevision: 3, ModRevision: 3, Version: 1},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo0"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 4, Version: 2},
},
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo1"), Value: []byte("bar"), CreateRevision: 3, ModRevision: 5, Version: 2},
},
}
@@ -916,7 +916,7 @@ func TestV3WatchProgressOnMemberRestart(t *testing.T) {
defer clus.Terminate(t)
client := clus.RandClient()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
errC := make(chan error, 1)
@@ -1029,7 +1029,7 @@ func testV3WatchMultipleStreams(t *testing.T, startRev int64) {
streams := make([]pb.Watch_WatchClient, 5)
for i := range streams {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, errW := wAPI.Watch(ctx)
if errW != nil {
@@ -1056,7 +1056,7 @@ func testV3WatchMultipleStreams(t *testing.T, startRev int64) {
}
}
- if _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
+ if _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}); err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
@@ -1064,7 +1064,7 @@ func testV3WatchMultipleStreams(t *testing.T, startRev int64) {
wg.Add(len(streams))
wevents := []*mvccpb.Event{
{
- Type: mvccpb.PUT,
+ Type: mvccpb.Event_PUT,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 2, ModRevision: 2, Version: 1},
},
}
@@ -1129,7 +1129,7 @@ func TestWatchWithProgressNotify(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
wStream, wErr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if wErr != nil {
@@ -1188,7 +1188,7 @@ func TestV3WatchClose(t *testing.T) {
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer func() {
wg.Done()
cancel()
@@ -1219,7 +1219,7 @@ func TestV3WatchWithFilter(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
ws, werr := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
@@ -1247,7 +1247,7 @@ func TestV3WatchWithFilter(t *testing.T) {
// put a key with empty value
kvc := integration.ToGRPC(clus.RandClient()).KV
preq := &pb.PutRequest{Key: []byte("foo")}
- _, err = kvc.Put(context.TODO(), preq)
+ _, err = kvc.Put(t.Context(), preq)
require.NoError(t, err)
select {
@@ -1257,14 +1257,14 @@ func TestV3WatchWithFilter(t *testing.T) {
}
dreq := &pb.DeleteRangeRequest{Key: []byte("foo")}
- _, err = kvc.DeleteRange(context.TODO(), dreq)
+ _, err = kvc.DeleteRange(t.Context(), dreq)
require.NoError(t, err)
select {
case resp := <-recv:
wevs := []*mvccpb.Event{
{
- Type: mvccpb.DELETE,
+ Type: mvccpb.Event_DELETE,
Kv: &mvccpb.KeyValue{Key: []byte("foo"), ModRevision: 3},
},
}
@@ -1281,7 +1281,7 @@ func TestV3WatchWithPrevKV(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- wctx, wcancel := context.WithCancel(context.Background())
+ wctx, wcancel := context.WithCancel(t.Context())
defer wcancel()
tests := []struct {
@@ -1299,7 +1299,7 @@ func TestV3WatchWithPrevKV(t *testing.T) {
}}
for i, tt := range tests {
kvc := integration.ToGRPC(clus.RandClient()).KV
- _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte(tt.key), Value: []byte(tt.vals[0])})
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte(tt.key), Value: []byte(tt.vals[0])})
require.NoError(t, err)
ws, werr := integration.ToGRPC(clus.RandClient()).Watch.Watch(wctx)
@@ -1317,7 +1317,7 @@ func TestV3WatchWithPrevKV(t *testing.T) {
_, err = ws.Recv()
require.NoError(t, err)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte(tt.key), Value: []byte(tt.vals[1])})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte(tt.key), Value: []byte(tt.vals[1])})
require.NoError(t, err)
recv := make(chan *pb.WatchResponse, 1)
@@ -1351,7 +1351,7 @@ func TestV3WatchCancellation(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
cli := clus.RandClient()
@@ -1391,7 +1391,7 @@ func TestV3WatchCloseCancelRace(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
cli := clus.RandClient()
@@ -1436,7 +1436,7 @@ func TestV3WatchProgressWaitsForSync(t *testing.T) {
defer clus.Terminate(t)
client := clus.RandClient()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
// Write a couple values into key to make sure there's a
@@ -1499,7 +1499,7 @@ func TestV3WatchProgressWaitsForSyncNoEvents(t *testing.T) {
defer clus.Terminate(t)
client := clus.RandClient()
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
resp, err := client.Put(ctx, "bar", "1")
@@ -1540,14 +1540,12 @@ func TestV3NoEventsLostOnCompact(t *testing.T) {
t.Skip("grpc proxy currently does not support requesting progress notifications")
}
integration.BeforeTest(t)
- if len(gofail.List()) == 0 {
- t.Skip("please run 'make gofail-enable' before running the test")
- }
+ integration.SkipIfNoGoFail(t)
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
client := clus.RandClient()
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
// sendLoop throughput is rate-limited to 1 event per second
diff --git a/tests/integration/clientv3/watch_fragment_test.go b/tests/integration/clientv3/watch/watch_fragment_test.go
similarity index 79%
rename from tests/integration/clientv3/watch_fragment_test.go
rename to tests/integration/clientv3/watch/watch_fragment_test.go
index 81450f5f9aa8..3c28e73d8a72 100644
--- a/tests/integration/clientv3/watch_fragment_test.go
+++ b/tests/integration/clientv3/watch/watch_fragment_test.go
@@ -14,18 +14,19 @@
//go:build !cluster_proxy
-package clientv3test
+package watch
import (
- "context"
"fmt"
"strings"
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"go.etcd.io/etcd/client/pkg/v3/testutil"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// TestWatchFragmentDisable ensures that large watch
@@ -63,23 +64,23 @@ func TestWatchFragmentEnableWithGRPCLimit(t *testing.T) {
// testWatchFragment triggers watch response that spans over multiple
// revisions exceeding server request limits when combined.
func testWatchFragment(t *testing.T, fragment, exceedRecvLimit bool) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cfg := &integration2.ClusterConfig{
+ cfg := &integration.ClusterConfig{
Size: 1,
MaxRequestBytes: 1.5 * 1024 * 1024,
}
if exceedRecvLimit {
cfg.ClientMaxCallRecvMsgSize = 1.5 * 1024 * 1024
}
- clus := integration2.NewCluster(t, cfg)
+ clus := integration.NewCluster(t, cfg)
defer clus.Terminate(t)
cli := clus.Client(0)
errc := make(chan error)
for i := 0; i < 10; i++ {
go func(i int) {
- _, err := cli.Put(context.TODO(),
+ _, err := cli.Put(t.Context(),
fmt.Sprint("foo", i),
strings.Repeat("a", 1024*1024),
)
@@ -87,39 +88,30 @@ func testWatchFragment(t *testing.T, fragment, exceedRecvLimit bool) {
}(i)
}
for i := 0; i < 10; i++ {
- if err := <-errc; err != nil {
- t.Fatalf("failed to put: %v", err)
- }
+ err := <-errc
+ require.NoErrorf(t, err, "failed to put")
}
opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithRev(1)}
if fragment {
opts = append(opts, clientv3.WithFragment())
}
- wch := cli.Watch(context.TODO(), "foo", opts...)
+ wch := cli.Watch(t.Context(), "foo", opts...)
// expect 10 MiB watch response
select {
case ws := <-wch:
// without fragment, should exceed gRPC client receive limit
if !fragment && exceedRecvLimit {
- if len(ws.Events) != 0 {
- t.Fatalf("expected 0 events with watch fragmentation, got %d", len(ws.Events))
- }
+ require.Emptyf(t, ws.Events, "expected 0 events with watch fragmentation")
exp := "code = ResourceExhausted desc = grpc: received message larger than max ("
- if !strings.Contains(ws.Err().Error(), exp) {
- t.Fatalf("expected 'ResourceExhausted' error, got %v", ws.Err())
- }
+ require.Containsf(t, ws.Err().Error(), exp, "expected 'ResourceExhausted' error")
return
}
// still expect merged watch events
- if len(ws.Events) != 10 {
- t.Fatalf("expected 10 events with watch fragmentation, got %d", len(ws.Events))
- }
- if ws.Err() != nil {
- t.Fatalf("unexpected error %v", ws.Err())
- }
+ require.Lenf(t, ws.Events, 10, "expected 10 events with watch fragmentation")
+ require.NoErrorf(t, ws.Err(), "unexpected error")
case <-time.After(testutil.RequestTimeout):
t.Fatalf("took too long to receive events")
diff --git a/tests/integration/clientv3/watch_test.go b/tests/integration/clientv3/watch/watch_test.go
similarity index 72%
rename from tests/integration/clientv3/watch_test.go
rename to tests/integration/clientv3/watch/watch_test.go
index b8481333382b..dbb3ecabaaa9 100644
--- a/tests/integration/clientv3/watch_test.go
+++ b/tests/integration/clientv3/watch/watch_test.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package clientv3test
+package watch
import (
"context"
@@ -27,20 +27,23 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/testing/protocmp"
mvccpb "go.etcd.io/etcd/api/v3/mvccpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/api/v3/version"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/etcdserver/api/v3rpc"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
type watcherTest func(*testing.T, *watchctx)
type watchctx struct {
- clus *integration2.Cluster
+ clus *integration.Cluster
w clientv3.Watcher
kv clientv3.KV
wclientMember int
@@ -49,9 +52,9 @@ type watchctx struct {
}
func runWatchTest(t *testing.T, f watcherTest) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
wclientMember := rand.Intn(3)
@@ -88,7 +91,7 @@ func testWatchMultiWatcher(t *testing.T, wctx *watchctx) {
for _, k := range keys {
// key watcher
go func(key string) {
- ch := wctx.w.Watch(context.TODO(), key)
+ ch := wctx.w.Watch(t.Context(), key)
if ch == nil {
t.Errorf("expected watcher channel, got nil")
}
@@ -109,7 +112,7 @@ func testWatchMultiWatcher(t *testing.T, wctx *watchctx) {
}
// prefix watcher on "b" (bar and baz)
go func() {
- prefixc := wctx.w.Watch(context.TODO(), "b", clientv3.WithPrefix())
+ prefixc := wctx.w.Watch(t.Context(), "b", clientv3.WithPrefix())
if prefixc == nil {
t.Errorf("expected watcher channel, got nil")
}
@@ -157,7 +160,7 @@ func testWatchMultiWatcher(t *testing.T, wctx *watchctx) {
<-readyc
}
// generate events
- ctx := context.TODO()
+ ctx := t.Context()
for i := 0; i < numKeyUpdates; i++ {
for _, k := range keys {
v := fmt.Sprintf("%s-%d", k, i)
@@ -173,9 +176,8 @@ func TestWatchRange(t *testing.T) {
}
func testWatchRange(t *testing.T, wctx *watchctx) {
- if wctx.ch = wctx.w.Watch(context.TODO(), "a", clientv3.WithRange("c")); wctx.ch == nil {
- t.Fatalf("expected non-nil channel")
- }
+ wctx.ch = wctx.w.Watch(t.Context(), "a", clientv3.WithRange("c"))
+ require.NotNilf(t, wctx.ch, "expected non-nil channel")
putAndWatch(t, wctx, "a", "a")
putAndWatch(t, wctx, "b", "b")
putAndWatch(t, wctx, "bar", "bar")
@@ -205,9 +207,8 @@ func testWatchReconnRequest(t *testing.T, wctx *watchctx) {
}
}()
// should reconnect when requesting watch
- if wctx.ch = wctx.w.Watch(context.TODO(), "a"); wctx.ch == nil {
- t.Fatalf("expected non-nil channel")
- }
+ wctx.ch = wctx.w.Watch(t.Context(), "a")
+ require.NotNilf(t, wctx.ch, "expected non-nil channel")
// wait for disconnections to stop
stopc <- struct{}{}
@@ -215,7 +216,7 @@ func testWatchReconnRequest(t *testing.T, wctx *watchctx) {
// spinning on dropping connections may trigger a leader election
// due to resource starvation; l-read to ensure the cluster is stable
- ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
_, err := wctx.kv.Get(ctx, "_")
require.NoError(t, err)
cancel()
@@ -231,9 +232,8 @@ func TestWatchReconnInit(t *testing.T) {
}
func testWatchReconnInit(t *testing.T, wctx *watchctx) {
- if wctx.ch = wctx.w.Watch(context.TODO(), "a"); wctx.ch == nil {
- t.Fatalf("expected non-nil channel")
- }
+ wctx.ch = wctx.w.Watch(t.Context(), "a")
+ require.NotNilf(t, wctx.ch, "expected non-nil channel")
wctx.clus.Members[wctx.wclientMember].Bridge().DropConnections()
// watcher should recover
putAndWatch(t, wctx, "a", "a")
@@ -246,9 +246,8 @@ func TestWatchReconnRunning(t *testing.T) {
}
func testWatchReconnRunning(t *testing.T, wctx *watchctx) {
- if wctx.ch = wctx.w.Watch(context.TODO(), "a"); wctx.ch == nil {
- t.Fatalf("expected non-nil channel")
- }
+ wctx.ch = wctx.w.Watch(t.Context(), "a")
+ require.NotNilf(t, wctx.ch, "expected non-nil channel")
putAndWatch(t, wctx, "a", "a")
// take down watcher connection
wctx.clus.Members[wctx.wclientMember].Bridge().DropConnections()
@@ -263,14 +262,12 @@ func TestWatchCancelImmediate(t *testing.T) {
}
func testWatchCancelImmediate(t *testing.T, wctx *watchctx) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
cancel()
wch := wctx.w.Watch(ctx, "a")
select {
case wresp, ok := <-wch:
- if ok {
- t.Fatalf("read wch got %v; expected closed channel", wresp)
- }
+ require.Falsef(t, ok, "read wch got %v; expected closed channel", wresp)
default:
t.Fatalf("closed watcher channel should not block")
}
@@ -282,18 +279,15 @@ func TestWatchCancelInit(t *testing.T) {
}
func testWatchCancelInit(t *testing.T, wctx *watchctx) {
- ctx, cancel := context.WithCancel(context.Background())
- if wctx.ch = wctx.w.Watch(ctx, "a"); wctx.ch == nil {
- t.Fatalf("expected non-nil watcher channel")
- }
+ ctx, cancel := context.WithCancel(t.Context())
+ wctx.ch = wctx.w.Watch(ctx, "a")
+ require.NotNilf(t, wctx.ch, "expected non-nil watcher channel")
cancel()
select {
case <-time.After(time.Second):
t.Fatalf("took too long to cancel")
case _, ok := <-wctx.ch:
- if ok {
- t.Fatalf("expected watcher channel to close")
- }
+ require.Falsef(t, ok, "expected watcher channel to close")
}
}
@@ -303,10 +297,9 @@ func TestWatchCancelRunning(t *testing.T) {
}
func testWatchCancelRunning(t *testing.T, wctx *watchctx) {
- ctx, cancel := context.WithCancel(context.Background())
- if wctx.ch = wctx.w.Watch(ctx, "a"); wctx.ch == nil {
- t.Fatalf("expected non-nil watcher channel")
- }
+ ctx, cancel := context.WithCancel(t.Context())
+ wctx.ch = wctx.w.Watch(ctx, "a")
+ require.NotNilf(t, wctx.ch, "expected non-nil watcher channel")
_, err := wctx.kv.Put(ctx, "a", "a")
require.NoError(t, err)
cancel()
@@ -323,50 +316,43 @@ func testWatchCancelRunning(t *testing.T, wctx *watchctx) {
case <-time.After(time.Second):
t.Fatalf("took too long to close")
case v, ok2 := <-wctx.ch:
- if ok2 {
- t.Fatalf("expected watcher channel to close, got %v", v)
- }
+ require.Falsef(t, ok2, "expected watcher channel to close, got %v", v)
}
}
}
func putAndWatch(t *testing.T, wctx *watchctx, key, val string) {
- _, err := wctx.kv.Put(context.TODO(), key, val)
+ _, err := wctx.kv.Put(t.Context(), key, val)
require.NoError(t, err)
select {
case <-time.After(5 * time.Second):
t.Fatalf("watch timed out")
case v, ok := <-wctx.ch:
- if !ok {
- t.Fatalf("unexpected watch close")
- }
- if err := v.Err(); err != nil {
- t.Fatalf("unexpected watch response error: %v", err)
- }
- if string(v.Events[0].Kv.Value) != val {
- t.Fatalf("bad value got %v, wanted %v", v.Events[0].Kv.Value, val)
- }
+ require.Truef(t, ok, "unexpected watch close")
+ err := v.Err()
+ require.NoErrorf(t, err, "unexpected watch response error")
+ require.Equalf(t, string(v.Events[0].Kv.Value), val, "bad value got %v, wanted %v", v.Events[0].Kv.Value, val)
}
}
// TestWatchResumeAfterDisconnect tests watch resume after member disconnects then connects.
// It ensures that correct events are returned corresponding to the start revision.
func TestWatchResumeAfterDisconnect(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
- _, err := cli.Put(context.TODO(), "b", "2")
+ _, err := cli.Put(t.Context(), "b", "2")
require.NoError(t, err)
- _, err = cli.Put(context.TODO(), "a", "3")
+ _, err = cli.Put(t.Context(), "a", "3")
require.NoError(t, err)
// if resume is broken, it'll pick up this key first instead of a=3
- _, err = cli.Put(context.TODO(), "a", "4")
+ _, err = cli.Put(t.Context(), "a", "4")
require.NoError(t, err)
// watch from revision 1
- wch := clus.Client(0).Watch(context.Background(), "a", clientv3.WithRev(1), clientv3.WithCreatedNotify())
+ wch := clus.Client(0).Watch(t.Context(), "a", clientv3.WithRev(1), clientv3.WithCreatedNotify())
// response for the create watch request, no events are in this response
// the current revision of etcd should be 4
if resp, ok := <-wch; !ok || resp.Header.Revision != 4 {
@@ -394,12 +380,8 @@ func TestWatchResumeAfterDisconnect(t *testing.T) {
if len(resp.Events) != 2 {
t.Fatal("expected two events on watch")
}
- if string(resp.Events[0].Kv.Value) != "3" {
- t.Fatalf("expected value=3, got event %+v", resp.Events[0])
- }
- if string(resp.Events[1].Kv.Value) != "4" {
- t.Fatalf("expected value=4, got event %+v", resp.Events[1])
- }
+ require.Equalf(t, "3", string(resp.Events[0].Kv.Value), "expected value=3, got event %+v", resp.Events[0])
+ require.Equalf(t, "4", string(resp.Events[1].Kv.Value), "expected value=4, got event %+v", resp.Events[1])
case <-time.After(5 * time.Second):
t.Fatal("watch timed out")
}
@@ -411,14 +393,14 @@ func TestWatchResumeAfterDisconnect(t *testing.T) {
// either a compaction error or all keys by staying in sync before the compaction
// is finally applied.
func TestWatchResumeCompacted(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, UseBridge: true})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
// create a waiting watcher at rev 1
w := clus.Client(0)
- wch := w.Watch(context.Background(), "foo", clientv3.WithRev(1))
+ wch := w.Watch(t.Context(), "foo", clientv3.WithRev(1))
select {
case w := <-wch:
t.Errorf("unexpected message from wch %v", w)
@@ -432,10 +414,10 @@ func TestWatchResumeCompacted(t *testing.T) {
numPuts := 5
kv := clus.Client(1)
for i := 0; i < numPuts; i++ {
- _, err := kv.Put(context.TODO(), "foo", "bar")
+ _, err := kv.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
}
- _, err := kv.Compact(context.TODO(), 3)
+ _, err := kv.Compact(t.Context(), 3)
require.NoError(t, err)
clus.Members[0].Restart(t)
@@ -450,16 +432,12 @@ func TestWatchResumeCompacted(t *testing.T) {
var ok bool
select {
case wresp, ok = <-wch:
- if !ok {
- t.Fatalf("expected wresp, but got closed channel")
- }
+ require.Truef(t, ok, "expected wresp, but got closed channel")
case <-time.After(5 * time.Second):
t.Fatalf("compacted watch timed out")
}
for _, ev := range wresp.Events {
- if ev.Kv.ModRevision != wRev {
- t.Fatalf("expected modRev %v, got %+v", wRev, ev)
- }
+ require.Equalf(t, ev.Kv.ModRevision, wRev, "expected modRev %v, got %+v", wRev, ev)
wRev++
}
if wresp.Err() == nil {
@@ -488,23 +466,23 @@ func TestWatchResumeCompacted(t *testing.T) {
// TestWatchCompactRevision ensures the CompactRevision error is given on a
// compaction event ahead of a watcher.
func TestWatchCompactRevision(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
// set some keys
kv := clus.RandClient()
for i := 0; i < 5; i++ {
- _, err := kv.Put(context.TODO(), "foo", "bar")
+ _, err := kv.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
}
w := clus.RandClient()
- _, err := kv.Compact(context.TODO(), 4)
+ _, err := kv.Compact(t.Context(), 4)
require.NoError(t, err)
- wch := w.Watch(context.Background(), "foo", clientv3.WithRev(2))
+ wch := w.Watch(t.Context(), "foo", clientv3.WithRev(2))
// get compacted error message
wresp, ok := <-wch
@@ -524,11 +502,11 @@ func TestWatchCompactRevision(t *testing.T) {
}
}
-func TestWatchWithProgressNotify(t *testing.T) { testWatchWithProgressNotify(t, true) }
+func TestWatchWithProgressNotify2(t *testing.T) { testWatchWithProgressNotify(t, true) }
func TestWatchWithProgressNotifyNoEvent(t *testing.T) { testWatchWithProgressNotify(t, false) }
func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// accelerate report interval so test terminates quickly
oldpi := v3rpc.GetProgressReportInterval()
@@ -537,7 +515,7 @@ func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
pi := 3 * time.Second
defer func() { v3rpc.SetProgressReportInterval(oldpi) }()
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
wc := clus.RandClient()
@@ -546,7 +524,7 @@ func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
if watchOnPut {
opts = append(opts, clientv3.WithPrefix())
}
- rch := wc.Watch(context.Background(), "foo", opts...)
+ rch := wc.Watch(t.Context(), "foo", opts...)
select {
case resp := <-rch: // wait for notification
@@ -558,7 +536,7 @@ func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
}
kvc := clus.RandClient()
- _, err := kvc.Put(context.TODO(), "foox", "bar")
+ _, err := kvc.Put(t.Context(), "foox", "bar")
require.NoError(t, err)
select {
@@ -583,18 +561,18 @@ func testWatchWithProgressNotify(t *testing.T, watchOnPut bool) {
}
func TestConfigurableWatchProgressNotifyInterval(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
progressInterval := 200 * time.Millisecond
- clus := integration2.NewCluster(t,
- &integration2.ClusterConfig{
+ clus := integration.NewCluster(t,
+ &integration.ClusterConfig{
Size: 3,
WatchProgressNotifyInterval: progressInterval,
})
defer clus.Terminate(t)
opts := []clientv3.OpOption{clientv3.WithProgressNotify()}
- rch := clus.RandClient().Watch(context.Background(), "foo", opts...)
+ rch := clus.RandClient().Watch(t.Context(), "foo", opts...)
timeout := 1 * time.Second // we expect to receive watch progress notify in 2 * progressInterval,
// but for CPU-starved situation it may take longer. So we use 1 second here for timeout.
@@ -609,7 +587,7 @@ func TestConfigurableWatchProgressNotifyInterval(t *testing.T) {
}
func TestWatchRequestProgress(t *testing.T) {
- if integration2.ThroughProxy {
+ if integration.ThroughProxy {
t.Skipf("grpc-proxy does not support WatchProgress yet")
}
testCases := []struct {
@@ -623,11 +601,11 @@ func TestWatchRequestProgress(t *testing.T) {
for _, c := range testCases {
t.Run(c.name, func(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
watchTimeout := 3 * time.Second
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
wc := clus.RandClient()
@@ -635,39 +613,33 @@ func TestWatchRequestProgress(t *testing.T) {
var watchChans []clientv3.WatchChan
for _, prefix := range c.watchers {
- watchChans = append(watchChans, wc.Watch(context.Background(), prefix, clientv3.WithPrefix()))
+ watchChans = append(watchChans, wc.Watch(t.Context(), prefix, clientv3.WithPrefix()))
}
- _, err := wc.Put(context.Background(), "/a", "1")
+ _, err := wc.Put(t.Context(), "/a", "1")
require.NoError(t, err)
for _, rch := range watchChans {
select {
case resp := <-rch: // wait for notification
- if len(resp.Events) != 1 {
- t.Fatalf("resp.Events expected 1, got %d", len(resp.Events))
- }
+ require.Lenf(t, resp.Events, 1, "resp.Events expected 1, got %d", len(resp.Events))
case <-time.After(watchTimeout):
t.Fatalf("watch response expected in %v, but timed out", watchTimeout)
}
}
// put a value not being watched to increment revision
- _, err = wc.Put(context.Background(), "x", "1")
+ _, err = wc.Put(t.Context(), "x", "1")
require.NoError(t, err)
- require.NoError(t, wc.RequestProgress(context.Background()))
+ require.NoError(t, wc.RequestProgress(t.Context()))
// verify all watch channels receive a progress notify
for _, rch := range watchChans {
select {
case resp := <-rch:
- if !resp.IsProgressNotify() {
- t.Fatalf("expected resp.IsProgressNotify() == true")
- }
- if resp.Header.Revision != 3 {
- t.Fatalf("resp.Header.Revision expected 3, got %d", resp.Header.Revision)
- }
+ require.Truef(t, resp.IsProgressNotify(), "expected resp.IsProgressNotify() == true")
+ require.Equalf(t, int64(3), resp.Header.Revision, "resp.Header.Revision expected 3, got %d", resp.Header.Revision)
case <-time.After(watchTimeout):
t.Fatalf("progress response expected in %v, but timed out", watchTimeout)
}
@@ -677,31 +649,25 @@ func TestWatchRequestProgress(t *testing.T) {
}
func TestWatchEventType(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer cluster.Terminate(t)
client := cluster.RandClient()
- ctx := context.Background()
+ ctx := t.Context()
watchChan := client.Watch(ctx, "/", clientv3.WithPrefix())
- if _, err := client.Put(ctx, "/toDelete", "foo"); err != nil {
- t.Fatalf("Put failed: %v", err)
- }
- if _, err := client.Put(ctx, "/toDelete", "bar"); err != nil {
- t.Fatalf("Put failed: %v", err)
- }
- if _, err := client.Delete(ctx, "/toDelete"); err != nil {
- t.Fatalf("Delete failed: %v", err)
- }
+ _, err := client.Put(ctx, "/toDelete", "foo")
+ require.NoErrorf(t, err, "Put failed: %v", err)
+ _, err = client.Put(ctx, "/toDelete", "bar")
+ require.NoErrorf(t, err, "Put failed: %v", err)
+ _, err = client.Delete(ctx, "/toDelete")
+ require.NoErrorf(t, err, "Delete failed: %v", err)
lcr, err := client.Lease.Grant(ctx, 1)
- if err != nil {
- t.Fatalf("lease create failed: %v", err)
- }
- if _, err := client.Put(ctx, "/toExpire", "foo", clientv3.WithLease(lcr.ID)); err != nil {
- t.Fatalf("Put failed: %v", err)
- }
+ require.NoErrorf(t, err, "lease create failed: %v", err)
+ _, err = client.Put(ctx, "/toExpire", "foo", clientv3.WithLease(lcr.ID))
+ require.NoErrorf(t, err, "Put failed: %v", err)
tests := []struct {
et mvccpb.Event_EventType
@@ -751,9 +717,9 @@ func TestWatchEventType(t *testing.T) {
}
func TestWatchErrConnClosed(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -761,7 +727,7 @@ func TestWatchErrConnClosed(t *testing.T) {
donec := make(chan struct{})
go func() {
defer close(donec)
- ch := cli.Watch(context.TODO(), "foo")
+ ch := cli.Watch(t.Context(), "foo")
if wr := <-ch; !IsCanceled(wr.Err()) {
t.Errorf("expected context canceled, got %v", wr.Err())
@@ -772,16 +738,31 @@ func TestWatchErrConnClosed(t *testing.T) {
clus.TakeClient(0)
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("wc.Watch took too long")
case <-donec:
}
}
+func IsCanceled(err error) bool {
+ if err == nil {
+ return false
+ }
+ if errors.Is(err, context.Canceled) {
+ return true
+ }
+ ev, ok := status.FromError(err)
+ if !ok {
+ return false
+ }
+ code := ev.Code()
+ return code == codes.Canceled
+}
+
func TestWatchAfterClose(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
@@ -790,14 +771,14 @@ func TestWatchAfterClose(t *testing.T) {
donec := make(chan struct{})
go func() {
- cli.Watch(context.TODO(), "foo")
+ cli.Watch(t.Context(), "foo")
if err := cli.Close(); err != nil && !errors.Is(err, context.Canceled) {
t.Errorf("expected %v, got %v", context.Canceled, err)
}
close(donec)
}()
select {
- case <-time.After(integration2.RequestWaitTimeout):
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("wc.Watch took too long")
case <-donec:
}
@@ -805,9 +786,9 @@ func TestWatchAfterClose(t *testing.T) {
// TestWatchWithRequireLeader checks the watch channel closes when no leader.
func TestWatchWithRequireLeader(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
// Put a key for the non-require leader watch to read as an event.
@@ -815,7 +796,7 @@ func TestWatchWithRequireLeader(t *testing.T) {
// ensure that it receives the update so watching after killing quorum
// is guaranteed to have the key.
liveClient := clus.Client(0)
- _, err := liveClient.Put(context.TODO(), "foo", "bar")
+ _, err := liveClient.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
clus.Members[1].Stop(t)
@@ -831,33 +812,26 @@ func TestWatchWithRequireLeader(t *testing.T) {
// so proxy tests receive a leader loss event on its existing watch before creating a new watch.
time.Sleep(time.Duration(5*clus.Members[0].ElectionTicks) * tickDuration)
- chLeader := liveClient.Watch(clientv3.WithRequireLeader(context.TODO()), "foo", clientv3.WithRev(1))
- chNoLeader := liveClient.Watch(context.TODO(), "foo", clientv3.WithRev(1))
+ chLeader := liveClient.Watch(clientv3.WithRequireLeader(t.Context()), "foo", clientv3.WithRev(1))
+ chNoLeader := liveClient.Watch(t.Context(), "foo", clientv3.WithRev(1))
select {
case resp, ok := <-chLeader:
- if !ok {
- t.Fatalf("expected %v watch channel, got closed channel", rpctypes.ErrNoLeader)
- }
- if !errors.Is(resp.Err(), rpctypes.ErrNoLeader) {
- t.Fatalf("expected %v watch response error, got %+v", rpctypes.ErrNoLeader, resp)
- }
- case <-time.After(integration2.RequestWaitTimeout):
+ require.Truef(t, ok, "expected %v watch channel, got closed channel", rpctypes.ErrNoLeader)
+ require.ErrorIsf(t, resp.Err(), rpctypes.ErrNoLeader, "expected %v watch response error, got %+v", rpctypes.ErrNoLeader, resp)
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("watch without leader took too long to close")
}
select {
case resp, ok := <-chLeader:
- if ok {
- t.Fatalf("expected closed channel, got response %v", resp)
- }
- case <-time.After(integration2.RequestWaitTimeout):
+ require.Falsef(t, ok, "expected closed channel, got response %v", resp)
+ case <-time.After(integration.RequestWaitTimeout):
t.Fatal("waited too long for channel to close")
}
- if _, ok := <-chNoLeader; !ok {
- t.Fatalf("expected response, got closed channel")
- }
+ _, ok := <-chNoLeader
+ require.Truef(t, ok, "expected response, got closed channel")
cnt, err := clus.Members[0].Metric(
"etcd_server_client_requests_total",
@@ -867,20 +841,18 @@ func TestWatchWithRequireLeader(t *testing.T) {
require.NoError(t, err)
cv, err := strconv.ParseInt(cnt, 10, 32)
require.NoError(t, err)
- if cv < 2 { // >2 when retried
- t.Fatalf("expected at least 2, got %q", cnt)
- }
+ require.GreaterOrEqualf(t, cv, int64(2), "expected at least 2, got %q", cnt)
}
// TestWatchWithFilter checks that watch filtering works.
func TestWatchWithFilter(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer cluster.Terminate(t)
client := cluster.RandClient()
- ctx := context.Background()
+ ctx := t.Context()
wcNoPut := client.Watch(ctx, "a", clientv3.WithFilterPut())
wcNoDel := client.Watch(ctx, "a", clientv3.WithFilterDelete())
@@ -911,42 +883,38 @@ func TestWatchWithFilter(t *testing.T) {
// TestWatchWithCreatedNotification checks that WithCreatedNotify returns a
// Created watch response.
func TestWatchWithCreatedNotification(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer cluster.Terminate(t)
client := cluster.RandClient()
- ctx := context.Background()
+ ctx := t.Context()
createC := client.Watch(ctx, "a", clientv3.WithCreatedNotify())
resp := <-createC
- if !resp.Created {
- t.Fatalf("expected created event, got %v", resp)
- }
+ require.Truef(t, resp.Created, "expected created event, got %v", resp)
}
// TestWatchWithCreatedNotificationDropConn ensures that
// a watcher with created notify does not post duplicate
// created events from disconnect.
func TestWatchWithCreatedNotificationDropConn(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer cluster.Terminate(t)
client := cluster.RandClient()
- wch := client.Watch(context.Background(), "a", clientv3.WithCreatedNotify())
+ wch := client.Watch(t.Context(), "a", clientv3.WithCreatedNotify())
resp := <-wch
- if !resp.Created {
- t.Fatalf("expected created event, got %v", resp)
- }
+ require.Truef(t, resp.Created, "expected created event, got %v", resp)
cluster.Members[0].Bridge().DropConnections()
@@ -960,11 +928,97 @@ func TestWatchWithCreatedNotificationDropConn(t *testing.T) {
}
}
+func TestWatchMixedPrevKVOnSameKeySeparateStreams(t *testing.T) {
+ integration.BeforeTest(t)
+
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer cluster.Terminate(t)
+
+ client := cluster.RandClient()
+ key := "prevkv-race-key"
+ const (
+ totalWatchers = 10
+ watchersWithPrev = 5
+ updates = 10
+ )
+
+ _, err := client.Put(t.Context(), key, "v0")
+ require.NoError(t, err)
+
+ type watchCase struct {
+ withPrev bool
+ cancel context.CancelFunc
+ ch clientv3.WatchChan
+ }
+ watches := make([]watchCase, totalWatchers)
+ for i := 0; i < totalWatchers; i++ {
+ // Different metadata forces the client to open independent watch streams.
+ md := metadata.Pairs("watch-stream-id", fmt.Sprintf("%d", i))
+ mctx := metadata.NewOutgoingContext(t.Context(), md)
+ wctx, cancel := context.WithCancel(mctx)
+
+ opts := []clientv3.OpOption{clientv3.WithCreatedNotify()}
+ withPrev := i < watchersWithPrev
+ if withPrev {
+ opts = append(opts, clientv3.WithPrevKV())
+ }
+
+ watches[i] = watchCase{
+ withPrev: withPrev,
+ cancel: cancel,
+ ch: client.Watch(wctx, key, opts...),
+ }
+ }
+ defer func() {
+ for _, watch := range watches {
+ watch.cancel()
+ }
+ }()
+
+ for i, watch := range watches {
+ resp := <-watch.ch
+ require.Truef(t, resp.Created, "watcher %d expected created response, got %+v", i, resp)
+ require.NoErrorf(t, resp.Err(), "watcher %d created response should not have error", i)
+ }
+
+ prevValue := "v0"
+ for rev := 1; rev <= updates; rev++ {
+ nextValue := fmt.Sprintf("v%d", rev)
+ _, err = client.Put(t.Context(), key, nextValue)
+ require.NoError(t, err)
+
+ for i, watch := range watches {
+ var resp clientv3.WatchResponse
+ select {
+ case resp = <-watch.ch:
+ case <-time.After(10 * time.Second):
+ t.Fatalf("watcher %d timed out waiting for watch response at revision %d", i, rev)
+ }
+
+ require.NoErrorf(t, resp.Err(), "watcher %d got unexpected watch error", i)
+ require.Lenf(t, resp.Events, 1, "watcher %d expected one event at revision %d", i, rev)
+
+ event := resp.Events[0]
+ require.Equalf(t, key, string(event.Kv.Key), "watcher %d received event for wrong key", i)
+ require.Equalf(t, nextValue, string(event.Kv.Value), "watcher %d got wrong value", i)
+
+ if watch.withPrev {
+ require.NotNilf(t, event.PrevKv, "watcher %d expected PrevKv", i)
+ require.Equalf(t, prevValue, string(event.PrevKv.Value), "watcher %d got wrong PrevKv value", i)
+ } else {
+ require.Nilf(t, event.PrevKv, "watcher %d should not receive PrevKv", i)
+ }
+ }
+
+ prevValue = nextValue
+ }
+}
+
// TestWatchCancelOnServer ensures client watcher cancels propagate back to the server.
func TestWatchCancelOnServer(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- cluster := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer cluster.Terminate(t)
client := cluster.RandClient()
@@ -975,7 +1029,7 @@ func TestWatchCancelOnServer(t *testing.T) {
// until require leader watches get create responses to ensure the leadership
// watches have started.
for {
- ctx, cancel := context.WithCancel(clientv3.WithRequireLeader(context.TODO()))
+ ctx, cancel := context.WithCancel(clientv3.WithRequireLeader(t.Context()))
ww := client.Watch(ctx, "a", clientv3.WithCreatedNotify())
wresp := <-ww
cancel()
@@ -988,7 +1042,7 @@ func TestWatchCancelOnServer(t *testing.T) {
for i := 0; i < numWatches; i++ {
// force separate streams in client
md := metadata.Pairs("some-key", fmt.Sprintf("%d", i))
- mctx := metadata.NewOutgoingContext(context.Background(), md)
+ mctx := metadata.NewOutgoingContext(t.Context(), md)
ctx, cancel := context.WithCancel(mctx)
cancels[i] = cancel
w := client.Watch(ctx, fmt.Sprintf("%d", i), clientv3.WithCreatedNotify())
@@ -1013,9 +1067,7 @@ func TestWatchCancelOnServer(t *testing.T) {
t.Fatalf("expected n=2 and err=nil, got n=%d and err=%v", n, serr)
}
- if maxWatchV-minWatchV < numWatches {
- t.Fatalf("expected %d canceled watchers, got %d", numWatches, maxWatchV-minWatchV)
- }
+ require.GreaterOrEqualf(t, maxWatchV-minWatchV, numWatches, "expected %d canceled watchers, got %d", numWatches, maxWatchV-minWatchV)
}
// TestWatchOverlapContextCancel stresses the watcher stream teardown path by
@@ -1028,20 +1080,20 @@ func TestWatchCancelOnServer(t *testing.T) {
// 4. watcher client finishes tearing down stream on "ctx"
// 5. w2 comes back canceled
func TestWatchOverlapContextCancel(t *testing.T) {
- f := func(clus *integration2.Cluster) {}
+ f := func(clus *integration.Cluster) {}
testWatchOverlapContextCancel(t, f)
}
func TestWatchOverlapDropConnContextCancel(t *testing.T) {
- f := func(clus *integration2.Cluster) {
+ f := func(clus *integration.Cluster) {
clus.Members[0].Bridge().DropConnections()
}
testWatchOverlapContextCancel(t, f)
}
-func testWatchOverlapContextCancel(t *testing.T, f func(*integration2.Cluster)) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+func testWatchOverlapContextCancel(t *testing.T, f func(*integration.Cluster)) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
n := 100
@@ -1049,17 +1101,17 @@ func testWatchOverlapContextCancel(t *testing.T, f func(*integration2.Cluster))
for i := range ctxs {
// make unique stream
md := metadata.Pairs("some-key", fmt.Sprintf("%d", i))
- ctxs[i] = metadata.NewOutgoingContext(context.Background(), md)
+ ctxs[i] = metadata.NewOutgoingContext(t.Context(), md)
// limits the maximum number of outstanding watchers per stream
ctxc[i] = make(chan struct{}, 2)
}
// issue concurrent watches on "abc" with cancel
cli := clus.RandClient()
- _, err := cli.Put(context.TODO(), "abc", "def")
+ _, err := cli.Put(t.Context(), "abc", "def")
require.NoError(t, err)
ch := make(chan struct{}, n)
- tCtx, cancelFunc := context.WithCancel(context.Background())
+ tCtx, cancelFunc := context.WithCancel(t.Context())
defer cancelFunc()
for i := 0; i < n; i++ {
go func() {
@@ -1108,11 +1160,11 @@ func testWatchOverlapContextCancel(t *testing.T, f func(*integration2.Cluster))
// TestWatchCancelAndCloseClient ensures that canceling a watcher then immediately
// closing the client does not return a client closing error.
func TestWatchCancelAndCloseClient(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
wch := cli.Watch(ctx, "abc")
donec := make(chan struct{})
go func() {
@@ -1136,12 +1188,12 @@ func TestWatchCancelAndCloseClient(t *testing.T) {
// to put them in resuming mode, cancels them so some resumes by cancel fail,
// then closes the watcher interface to ensure correct clean up.
func TestWatchStressResumeClose(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1, UseBridge: true})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
defer clus.Terminate(t)
cli := clus.Client(0)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
// add more watches than can be resumed before the cancel
wchs := make([]clientv3.WatchChan, 2000)
for i := range wchs {
@@ -1156,11 +1208,11 @@ func TestWatchStressResumeClose(t *testing.T) {
// TestWatchCancelDisconnected ensures canceling a watcher works when
// its grpc stream is disconnected / reconnecting.
func TestWatchCancelDisconnected(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
// add more watches than can be resumed before the cancel
wch := cli.Watch(ctx, "abc")
clus.Members[0].Stop(t)
@@ -1178,24 +1230,18 @@ func TestWatchClose(t *testing.T) {
}
func testWatchClose(t *testing.T, wctx *watchctx) {
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
wch := wctx.w.Watch(ctx, "a")
cancel()
- if wch == nil {
- t.Fatalf("expected watcher channel, got nil")
- }
- if wctx.w.Close() != nil {
- t.Fatalf("watch did not close successfully")
- }
+ require.NotNilf(t, wch, "expected watcher channel, got nil")
+ require.NoErrorf(t, wctx.w.Close(), "watch did not close successfully")
wresp, ok := <-wch
- if ok {
- t.Fatalf("read wch got %v; expected closed channel", wresp)
- }
+ require.Falsef(t, ok, "read wch got %v; expected closed channel", wresp)
}
func TestWatch(t *testing.T) {
- integration2.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
t.Cleanup(func() { clus.Terminate(t) })
client := clus.Client(0)
@@ -1242,7 +1288,7 @@ func TestWatch(t *testing.T) {
} else {
require.ErrorContains(t, err, tc.wantError.Error())
}
- if diff := cmp.Diff(tc.wantEvents, events); diff != "" {
+ if diff := cmp.Diff(tc.wantEvents, events, protocmp.Transform()); diff != "" {
t.Errorf("unexpected events (-want +got):\n%s", diff)
}
})
diff --git a/tests/integration/cluster_test.go b/tests/integration/cluster_test.go
index 732f5e2e19dc..8b98efd6cf9e 100644
--- a/tests/integration/cluster_test.go
+++ b/tests/integration/cluster_test.go
@@ -130,25 +130,19 @@ func TestForceNewCluster(t *testing.T) {
c := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer c.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
resp, err := c.Members[0].Client.Put(ctx, "/foo", "bar")
- if err != nil {
- t.Fatalf("unexpected create error: %v", err)
- }
+ require.NoErrorf(t, err, "unexpected create error")
cancel()
// ensure create has been applied in this machine
- ctx, cancel = context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel = context.WithTimeout(t.Context(), integration.RequestTimeout)
watch := c.Members[0].Client.Watcher.Watch(ctx, "/foo", clientv3.WithRev(resp.Header.Revision-1))
for resp := range watch {
if len(resp.Events) != 0 {
break
}
- if resp.Err() != nil {
- t.Fatalf("unexpected watch error: %q", resp.Err())
- }
- if resp.Canceled {
- t.Fatalf("watch cancelled")
- }
+ require.NoErrorf(t, resp.Err(), "unexpected watch error")
+ require.Falsef(t, resp.Canceled, "watch cancelled")
}
cancel()
@@ -157,25 +151,19 @@ func TestForceNewCluster(t *testing.T) {
c.Members[2].Terminate(t)
c.Members[0].ForceNewCluster = true
err = c.Members[0].Restart(t)
- if err != nil {
- t.Fatalf("unexpected ForceRestart error: %v", err)
- }
+ require.NoErrorf(t, err, "unexpected ForceRestart error")
c.WaitMembersForLeader(t, c.Members[:1])
// use new http client to init new connection
// ensure force restart keep the old data, and new Cluster can make progress
- ctx, cancel = context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel = context.WithTimeout(t.Context(), integration.RequestTimeout)
watch = c.Members[0].Client.Watcher.Watch(ctx, "/foo", clientv3.WithRev(resp.Header.Revision-1))
for resp := range watch {
if len(resp.Events) != 0 {
break
}
- if resp.Err() != nil {
- t.Fatalf("unexpected watch error: %q", resp.Err())
- }
- if resp.Canceled {
- t.Fatalf("watch cancelled")
- }
+ require.NoErrorf(t, resp.Err(), "unexpected watch error")
+ require.Falsef(t, resp.Canceled, "watch cancelled")
}
cancel()
clusterMustProgress(t, c.Members[:1])
@@ -252,7 +240,7 @@ func TestIssue2904(t *testing.T) {
c.Members[2].Stop(t)
// send remove member-1 request to the Cluster.
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
// the proposal is not committed because member 1 is stopped, but the
// proposal is appended to leader'Server raft log.
c.Members[0].Client.MemberRemove(ctx, uint64(c.Members[2].Server.MemberID()))
@@ -324,10 +312,9 @@ func TestIssue3699(t *testing.T) {
t.Logf("Expecting successful put...")
// try to participate in Cluster
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
- if _, err := c.Members[0].Client.Put(ctx, "/foo", "bar"); err != nil {
- t.Fatalf("unexpected error on Put (%v)", err)
- }
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
+ _, err := c.Members[0].Client.Put(ctx, "/foo", "bar")
+ require.NoErrorf(t, err, "unexpected error on Put")
cancel()
}
@@ -344,9 +331,7 @@ func TestRejectUnhealthyAdd(t *testing.T) {
// all attempts to add member should fail
for i := 1; i < len(c.Members); i++ {
err := c.AddMemberByURL(t, c.Members[i].Client, "unix://foo:12345")
- if err == nil {
- t.Fatalf("should have failed adding peer")
- }
+ require.Errorf(t, err, "should have failed adding peer")
// TODO: client should return descriptive error codes for internal errors
if !strings.Contains(err.Error(), "unhealthy cluster") {
t.Errorf("unexpected error (%v)", err)
@@ -365,9 +350,7 @@ func TestRejectUnhealthyAdd(t *testing.T) {
break
}
}
- if err != nil {
- t.Fatalf("should have added peer to healthy Cluster (%v)", err)
- }
+ require.NoErrorf(t, err, "should have added peer to healthy Cluster (%v)", err)
}
// TestRejectUnhealthyRemove ensures an unhealthy cluster rejects removing members
@@ -384,9 +367,7 @@ func TestRejectUnhealthyRemove(t *testing.T) {
// reject remove active member since (3,2)-(1,0) => (2,2) lacks quorum
err := c.RemoveMember(t, c.Members[leader].Client, uint64(c.Members[2].Server.MemberID()))
- if err == nil {
- t.Fatalf("should reject quorum breaking remove: %s", err)
- }
+ require.Errorf(t, err, "should reject quorum breaking remove: %s", err)
// TODO: client should return more descriptive error codes for internal errors
if !strings.Contains(err.Error(), "unhealthy cluster") {
t.Errorf("unexpected error (%v)", err)
@@ -396,9 +377,8 @@ func TestRejectUnhealthyRemove(t *testing.T) {
time.Sleep(time.Duration(integration.ElectionTicks * int(config.TickDuration)))
// permit remove dead member since (3,2) - (0,1) => (3,1) has quorum
- if err = c.RemoveMember(t, c.Members[2].Client, uint64(c.Members[0].Server.MemberID())); err != nil {
- t.Fatalf("should accept removing down member: %s", err)
- }
+ err = c.RemoveMember(t, c.Members[2].Client, uint64(c.Members[0].Server.MemberID()))
+ require.NoErrorf(t, err, "should accept removing down member")
// bring cluster to (4,1)
c.Members[0].Restart(t)
@@ -407,9 +387,8 @@ func TestRejectUnhealthyRemove(t *testing.T) {
time.Sleep((3 * etcdserver.HealthInterval) / 2)
// accept remove member since (4,1)-(1,0) => (3,1) has quorum
- if err = c.RemoveMember(t, c.Members[1].Client, uint64(c.Members[0].Server.MemberID())); err != nil {
- t.Fatalf("expected to remove member, got error %v", err)
- }
+ err = c.RemoveMember(t, c.Members[1].Client, uint64(c.Members[0].Server.MemberID()))
+ require.NoErrorf(t, err, "expected to remove member, got error")
}
// TestRestartRemoved ensures that restarting removed member must exit
@@ -431,17 +410,15 @@ func TestRestartRemoved(t *testing.T) {
firstMember.KeepDataDirTerminate = true
// 3. remove first member, shut down without deleting data
- if err := c.RemoveMember(t, c.Members[1].Client, uint64(firstMember.Server.MemberID())); err != nil {
- t.Fatalf("expected to remove member, got error %v", err)
- }
+ err := c.RemoveMember(t, c.Members[1].Client, uint64(firstMember.Server.MemberID()))
+ require.NoErrorf(t, err, "expected to remove member, got error")
c.WaitLeader(t)
// 4. restart first member with 'initial-cluster-state=new'
// wrong config, expects exit within ReqTimeout
firstMember.ServerConfig.NewCluster = false
- if err := firstMember.Restart(t); err != nil {
- t.Fatalf("unexpected ForceRestart error: %v", err)
- }
+ err = firstMember.Restart(t)
+ require.NoErrorf(t, err, "unexpected ForceRestart error")
defer func() {
firstMember.Close()
os.RemoveAll(firstMember.ServerConfig.DataDir)
@@ -464,7 +441,7 @@ func clusterMustProgress(t *testing.T, members []*integration.Member) {
)
// retry in case of leader loss induced by slow CI
for i := 0; i < 3; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
resp, err = members[0].Client.Put(ctx, key, "bar")
cancel()
if err == nil {
@@ -472,23 +449,17 @@ func clusterMustProgress(t *testing.T, members []*integration.Member) {
}
t.Logf("failed to create key on #0 (%v)", err)
}
- if err != nil {
- t.Fatalf("create on #0 error: %v", err)
- }
+ require.NoErrorf(t, err, "create on #0 error")
for i, m := range members {
- mctx, mcancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ mctx, mcancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
watch := m.Client.Watcher.Watch(mctx, key, clientv3.WithRev(resp.Header.Revision-1))
for resp := range watch {
if len(resp.Events) != 0 {
break
}
- if resp.Err() != nil {
- t.Fatalf("#%d: watch error: %q", i, resp.Err())
- }
- if resp.Canceled {
- t.Fatalf("#%d: watch: cancelled", i)
- }
+ require.NoErrorf(t, resp.Err(), "#%d: watch error", i)
+ require.Falsef(t, resp.Canceled, "#%d: watch: cancelled", i)
}
mcancel()
}
@@ -523,16 +494,16 @@ func TestConcurrentRemoveMember(t *testing.T) {
c := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer c.Terminate(t)
- addResp, err := c.Members[0].Client.MemberAddAsLearner(context.Background(), []string{"http://localhost:123"})
+ addResp, err := c.Members[0].Client.MemberAddAsLearner(t.Context(), []string{"http://localhost:123"})
require.NoError(t, err)
removeID := addResp.Member.ID
done := make(chan struct{})
go func() {
time.Sleep(time.Second / 2)
- c.Members[0].Client.MemberRemove(context.Background(), removeID)
+ c.Members[0].Client.MemberRemove(t.Context(), removeID)
close(done)
}()
- _, err = c.Members[0].Client.MemberRemove(context.Background(), removeID)
+ _, err = c.Members[0].Client.MemberRemove(t.Context(), removeID)
require.NoError(t, err)
<-done
}
@@ -542,16 +513,16 @@ func TestConcurrentMoveLeader(t *testing.T) {
c := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer c.Terminate(t)
- addResp, err := c.Members[0].Client.MemberAddAsLearner(context.Background(), []string{"http://localhost:123"})
+ addResp, err := c.Members[0].Client.MemberAddAsLearner(t.Context(), []string{"http://localhost:123"})
require.NoError(t, err)
removeID := addResp.Member.ID
done := make(chan struct{})
go func() {
time.Sleep(time.Second / 2)
- c.Members[0].Client.MoveLeader(context.Background(), removeID)
+ c.Members[0].Client.MoveLeader(t.Context(), removeID)
close(done)
}()
- _, err = c.Members[0].Client.MemberRemove(context.Background(), removeID)
+ _, err = c.Members[0].Client.MemberRemove(t.Context(), removeID)
require.NoError(t, err)
<-done
}
diff --git a/tests/integration/corrupt_test.go b/tests/integration/corrupt_test.go
index ddd7234e2d15..ddbf2d3e6d9f 100644
--- a/tests/integration/corrupt_test.go
+++ b/tests/integration/corrupt_test.go
@@ -38,7 +38,7 @@ func TestPeriodicCheck(t *testing.T) {
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
var totalRevisions int64 = 1210
var rev int64
@@ -74,7 +74,7 @@ func TestPeriodicCheckDetectsCorruption(t *testing.T) {
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
for i := 0; i < 10; i++ {
_, err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i))
@@ -112,7 +112,7 @@ func TestCompactHashCheck(t *testing.T) {
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
var totalRevisions int64 = 1210
var rev int64
@@ -149,7 +149,7 @@ func TestCompactHashCheckDetectCorruption(t *testing.T) {
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
for i := 0; i < 10; i++ {
_, err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i))
@@ -186,7 +186,7 @@ func TestCompactHashCheckDetectMultipleCorruption(t *testing.T) {
cc, err := clus.ClusterClient(t)
require.NoError(t, err)
- ctx := context.Background()
+ ctx := t.Context()
for i := 0; i < 10; i++ {
_, err = cc.Put(ctx, testutil.PickKey(int64(i)), fmt.Sprint(i))
diff --git a/tests/integration/embed/embed_test.go b/tests/integration/embed/embed_test.go
index 242f2b6a7cad..bab48dbf2bdf 100644
--- a/tests/integration/embed/embed_test.go
+++ b/tests/integration/embed/embed_test.go
@@ -20,7 +20,6 @@
package embed_test
import (
- "context"
"fmt"
"net/url"
"os"
@@ -36,7 +35,7 @@ import (
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/embed"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
@@ -57,7 +56,6 @@ func TestEmbedEtcd(t *testing.T) {
wpeers int
wclients int
}{
- {werr: "multiple discovery"},
{werr: "advertise-client-urls is required"},
{werr: "should be at least"},
{werr: "is too long"},
@@ -77,18 +75,17 @@ func TestEmbedEtcd(t *testing.T) {
tests[i].cfg.LogOutputs = []string{"/dev/null"}
}
- tests[0].cfg.Durl = "abc"
- setupEmbedCfg(&tests[1].cfg, []url.URL{urls[0]}, []url.URL{urls[1]})
- tests[1].cfg.AdvertiseClientUrls = nil
- tests[2].cfg.TickMs = tests[2].cfg.ElectionMs - 1
- tests[3].cfg.ElectionMs = 999999
- setupEmbedCfg(&tests[4].cfg, []url.URL{urls[2]}, []url.URL{urls[3]})
- setupEmbedCfg(&tests[5].cfg, []url.URL{urls[4]}, []url.URL{urls[5], urls[6]})
- setupEmbedCfg(&tests[6].cfg, []url.URL{urls[7], urls[8]}, []url.URL{urls[9]})
+ setupEmbedCfg(&tests[0].cfg, []url.URL{urls[0]}, []url.URL{urls[1]})
+ tests[0].cfg.AdvertiseClientUrls = nil
+ tests[1].cfg.TickMs = tests[2].cfg.ElectionMs - 1
+ tests[2].cfg.ElectionMs = 999999
+ setupEmbedCfg(&tests[3].cfg, []url.URL{urls[2]}, []url.URL{urls[3]})
+ setupEmbedCfg(&tests[4].cfg, []url.URL{urls[4]}, []url.URL{urls[5], urls[6]})
+ setupEmbedCfg(&tests[5].cfg, []url.URL{urls[7], urls[8]}, []url.URL{urls[9]})
dnsURL, _ := url.Parse("http://whatever.test:12345")
- tests[7].cfg.ListenClientUrls = []url.URL{*dnsURL}
- tests[8].cfg.ListenPeerUrls = []url.URL{*dnsURL}
+ tests[6].cfg.ListenClientUrls = []url.URL{*dnsURL}
+ tests[7].cfg.ListenPeerUrls = []url.URL{*dnsURL}
dir := filepath.Join(t.TempDir(), "embed-etcd")
@@ -157,12 +154,12 @@ func testEmbedEtcdGracefulStop(t *testing.T, secure bool) {
clientCfg.TLS, err = testTLSInfo.ClientConfig()
require.NoError(t, err)
}
- cli, err := integration2.NewClient(t, clientCfg)
+ cli, err := integration.NewClient(t, clientCfg)
require.NoError(t, err)
defer cli.Close()
// open watch connection
- cli.Watch(context.Background(), "foo")
+ cli.Watch(t.Context(), "foo")
donec := make(chan struct{})
go func() {
@@ -221,7 +218,7 @@ func TestEmbedEtcdAutoCompactionRetentionRetained(t *testing.T) {
}
func TestEmbedEtcdStopDuringBootstrapping(t *testing.T) {
- integration2.BeforeTest(t, integration2.WithFailpoint("beforePublishing", `sleep("2s")`))
+ integration.BeforeTest(t, integration.WithFailpoint("beforePublishing", `sleep("2s")`))
done := make(chan struct{})
go func() {
diff --git a/tests/integration/fixtures-expired/gencerts.sh b/tests/integration/fixtures-expired/gencerts.sh
index 8eea747ffbdf..1fe32155276c 100755
--- a/tests/integration/fixtures-expired/gencerts.sh
+++ b/tests/integration/fixtures-expired/gencerts.sh
@@ -21,7 +21,7 @@ fi
cfssl gencert --initca=true ./ca-csr.json | cfssljson --bare ./ca
mv ca.pem ca.crt
-if which openssl >/dev/null; then
+if command -v openssl >/dev/null; then
openssl x509 -in ca.crt -noout -text
fi
@@ -43,7 +43,7 @@ cfssl gencert \
mv server-ip.pem server-ip.crt
mv server-ip-key.pem server-ip.key.insecure
-if which openssl >/dev/null; then
+if command -v openssl >/dev/null; then
openssl x509 -in ./server.crt -text -noout
openssl x509 -in ./server-ip.crt -text -noout
fi
diff --git a/tests/integration/grpc_test.go b/tests/integration/grpc_test.go
deleted file mode 100644
index e3b762c31efc..000000000000
--- a/tests/integration/grpc_test.go
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2021 The etcd Authors
-//
-// 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 integration
-
-import (
- "context"
- tls "crypto/tls"
- "fmt"
- "strings"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
- "google.golang.org/grpc"
-
- clientv3 "go.etcd.io/etcd/client/v3"
- "go.etcd.io/etcd/tests/v3/framework/integration"
-)
-
-func TestAuthority(t *testing.T) {
- tcs := []struct {
- name string
- useTCP bool
- useTLS bool
- clientURLPattern string
- expectAuthorityPattern string
- }{
- {
- name: "unix:path",
- clientURLPattern: "unix:localhost:${MEMBER_NAME}",
- expectAuthorityPattern: "localhost:${MEMBER_NAME}",
- },
- {
- name: "unix://absolute_path",
- clientURLPattern: "unix://localhost:${MEMBER_NAME}",
- expectAuthorityPattern: "localhost:${MEMBER_NAME}",
- },
- // "unixs" is not standard schema supported by etcd
- {
- name: "unixs:absolute_path",
- useTLS: true,
- clientURLPattern: "unixs:localhost:${MEMBER_NAME}",
- expectAuthorityPattern: "localhost:${MEMBER_NAME}",
- },
- {
- name: "unixs://absolute_path",
- useTLS: true,
- clientURLPattern: "unixs://localhost:${MEMBER_NAME}",
- expectAuthorityPattern: "localhost:${MEMBER_NAME}",
- },
- {
- name: "http://domain[:port]",
- useTCP: true,
- clientURLPattern: "http://localhost:${MEMBER_PORT}",
- expectAuthorityPattern: "localhost:${MEMBER_PORT}",
- },
- {
- name: "https://domain[:port]",
- useTLS: true,
- useTCP: true,
- clientURLPattern: "https://localhost:${MEMBER_PORT}",
- expectAuthorityPattern: "localhost:${MEMBER_PORT}",
- },
- {
- name: "http://address[:port]",
- useTCP: true,
- clientURLPattern: "http://127.0.0.1:${MEMBER_PORT}",
- expectAuthorityPattern: "127.0.0.1:${MEMBER_PORT}",
- },
- {
- name: "https://address[:port]",
- useTCP: true,
- useTLS: true,
- clientURLPattern: "https://127.0.0.1:${MEMBER_PORT}",
- expectAuthorityPattern: "127.0.0.1:${MEMBER_PORT}",
- },
- }
- for _, tc := range tcs {
- for _, clusterSize := range []int{1, 3} {
- t.Run(fmt.Sprintf("Size: %d, Scenario: %q", clusterSize, tc.name), func(t *testing.T) {
- integration.BeforeTest(t)
- cfg := integration.ClusterConfig{
- Size: clusterSize,
- UseTCP: tc.useTCP,
- UseIP: tc.useTCP,
- }
- cfg, tlsConfig := setupTLS(t, tc.useTLS, cfg)
- clus := integration.NewCluster(t, &cfg)
- defer clus.Terminate(t)
-
- kv := setupClient(t, tc.clientURLPattern, clus, tlsConfig)
- defer kv.Close()
-
- putRequestMethod := "/etcdserverpb.KV/Put"
- for i := 0; i < 100; i++ {
- _, err := kv.Put(context.TODO(), "foo", "bar")
- require.NoError(t, err)
- }
-
- assertAuthority(t, tc.expectAuthorityPattern, clus, putRequestMethod)
- })
- }
- }
-}
-
-func setupTLS(t *testing.T, useTLS bool, cfg integration.ClusterConfig) (integration.ClusterConfig, *tls.Config) {
- t.Helper()
- if useTLS {
- cfg.ClientTLS = &integration.TestTLSInfo
- tlsConfig, err := integration.TestTLSInfo.ClientConfig()
- require.NoError(t, err)
- return cfg, tlsConfig
- }
- return cfg, nil
-}
-
-func setupClient(t *testing.T, endpointPattern string, clus *integration.Cluster, tlsConfig *tls.Config) *clientv3.Client {
- t.Helper()
- endpoints := templateEndpoints(t, endpointPattern, clus)
- kv, err := clientv3.New(clientv3.Config{
- Endpoints: endpoints,
- DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
- TLS: tlsConfig,
- })
- require.NoError(t, err)
- return kv
-}
-
-func templateEndpoints(t *testing.T, pattern string, clus *integration.Cluster) []string {
- t.Helper()
- var endpoints []string
- for _, m := range clus.Members {
- ent := pattern
- ent = strings.ReplaceAll(ent, "${MEMBER_PORT}", m.GRPCPortNumber())
- ent = strings.ReplaceAll(ent, "${MEMBER_NAME}", m.Name)
- endpoints = append(endpoints, ent)
- }
- return endpoints
-}
-
-func templateAuthority(t *testing.T, pattern string, m *integration.Member) string {
- t.Helper()
- authority := pattern
- authority = strings.ReplaceAll(authority, "${MEMBER_PORT}", m.GRPCPortNumber())
- authority = strings.ReplaceAll(authority, "${MEMBER_NAME}", m.Name)
- return authority
-}
-
-func assertAuthority(t *testing.T, expectedAuthorityPattern string, clus *integration.Cluster, filterMethod string) {
- t.Helper()
- for _, m := range clus.Members {
- requestsFound := 0
- expectedAuthority := templateAuthority(t, expectedAuthorityPattern, m)
- for _, r := range m.RecordedRequests() {
- if filterMethod != "" && r.FullMethod != filterMethod {
- continue
- }
- if r.Authority == expectedAuthority {
- requestsFound++
- } else {
- t.Errorf("Got unexpected authority header, member: %q, request: %q, got authority: %q, expected %q", m.Name, r.FullMethod, r.Authority, expectedAuthority)
- }
- }
- if requestsFound == 0 {
- t.Errorf("Expect at least one request with matched authority header value was recorded by the server intercepter on member %s but got 0", m.Name)
- }
- }
-}
diff --git a/tests/integration/hashkv_test.go b/tests/integration/hashkv_test.go
deleted file mode 100644
index 2aa8cc69e058..000000000000
--- a/tests/integration/hashkv_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2022 The etcd Authors
-//
-// 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 integration
-
-import (
- "context"
- "net"
- "net/http"
- "testing"
-
- "github.com/stretchr/testify/require"
-
- clientv3 "go.etcd.io/etcd/client/v3"
- "go.etcd.io/etcd/server/v3/etcdserver"
- "go.etcd.io/etcd/server/v3/storage/mvcc/testutil"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
-)
-
-// TestCompactionHash tests the compaction hash
-// TODO: Change this to fuzz test
-func TestCompactionHash(t *testing.T) {
- integration2.BeforeTest(t)
-
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
- defer clus.Terminate(t)
-
- cc, err := clus.ClusterClient(t)
- require.NoError(t, err)
- client := &http.Client{
- Transport: &http.Transport{
- DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
- return net.Dial("unix", clus.Members[0].PeerURLs[0].Host)
- },
- },
- }
-
- testutil.TestCompactionHash(context.Background(), t, hashTestCase{cc, clus.Members[0].GRPCURL, client, clus.Members[0].Server}, 1000)
-}
-
-type hashTestCase struct {
- *clientv3.Client
- url string
- http *http.Client
- server *etcdserver.EtcdServer
-}
-
-func (tc hashTestCase) Put(ctx context.Context, key, value string) error {
- _, err := tc.Client.Put(ctx, key, value)
- return err
-}
-
-func (tc hashTestCase) Delete(ctx context.Context, key string) error {
- _, err := tc.Client.Delete(ctx, key)
- return err
-}
-
-func (tc hashTestCase) HashByRev(ctx context.Context, rev int64) (testutil.KeyValueHash, error) {
- resp, err := etcdserver.HashByRev(ctx, tc.server.Cluster().ID(), tc.http, "http://unix", rev)
- return testutil.KeyValueHash{Hash: resp.Hash, CompactRevision: resp.CompactRevision, Revision: resp.Header.Revision}, err
-}
-
-func (tc hashTestCase) Defrag(ctx context.Context) error {
- _, err := tc.Client.Defragment(ctx, tc.url)
- return err
-}
-
-func (tc hashTestCase) Compact(ctx context.Context, rev int64) error {
- _, err := tc.Client.Compact(ctx, rev, clientv3.WithCompactPhysical())
- return err
-}
diff --git a/tests/integration/main_test.go b/tests/integration/main_test.go
index e783205834f5..8d9922dc6919 100644
--- a/tests/integration/main_test.go
+++ b/tests/integration/main_test.go
@@ -1,15 +1,29 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// Copyright 2022 The etcd Authors
+//
+// 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 integration
import (
+ "os"
"testing"
+ "google.golang.org/grpc/grpclog"
+
"go.etcd.io/etcd/client/pkg/v3/testutil"
)
func TestMain(m *testing.M) {
testutil.MustTestMainWithLeakDetection(m)
+ grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
}
diff --git a/tests/integration/member_test.go b/tests/integration/member_test.go
index 4d8735d3d95b..1616df299885 100644
--- a/tests/integration/member_test.go
+++ b/tests/integration/member_test.go
@@ -73,7 +73,7 @@ func TestLaunchDuplicateMemberShouldFail(t *testing.T) {
defer c.Terminate(t)
if err := m.Launch(); err == nil {
- t.Errorf("unexpect successful launch")
+ t.Errorf("unexpected successful launch")
} else {
t.Logf("launch failed as expected: %v", err)
assert.Contains(t, err.Error(), "has already been bootstrapped")
@@ -91,12 +91,10 @@ func TestSnapshotAndRestartMember(t *testing.T) {
var err error
for i := 0; i < 120; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
key := fmt.Sprintf("foo%d", i)
_, err = m.Client.Put(ctx, "/"+key, "bar")
- if err != nil {
- t.Fatalf("#%d: create on %s error: %v", i, m.URL(), err)
- }
+ require.NoErrorf(t, err, "#%d: create on %s error", i, m.URL())
cancel()
}
m.Stop(t)
@@ -104,12 +102,10 @@ func TestSnapshotAndRestartMember(t *testing.T) {
m.WaitOK(t)
for i := 0; i < 120; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
key := fmt.Sprintf("foo%d", i)
resp, err := m.Client.Get(ctx, "/"+key)
- if err != nil {
- t.Fatalf("#%d: get on %s error: %v", i, m.URL(), err)
- }
+ require.NoErrorf(t, err, "#%d: get on %s error", i, m.URL())
cancel()
if len(resp.Kvs) != 1 || string(resp.Kvs[0].Value) != "bar" {
@@ -149,7 +145,7 @@ func TestRemoveMemberAndWALReplay(t *testing.T) {
// Add some k/v to trigger snapshot
for i := 0; i < 15; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), integration.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestTimeout)
_, err := c.Members[0].Client.Put(ctx, fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i))
cancel()
require.NoErrorf(t, err, "failed to put key-value")
@@ -178,7 +174,7 @@ func checkMemberCount(t *testing.T, m *integration.Member, expectedMemberCount i
if len(membersFromBackend) != expectedMemberCount {
t.Errorf("Expect member count read from backend=%d, got %d", expectedMemberCount, len(membersFromBackend))
}
- membersResp, err := m.Client.MemberList(context.Background())
+ membersResp, err := m.Client.MemberList(t.Context())
require.NoError(t, err)
if len(membersResp.Members) != expectedMemberCount {
t.Errorf("Expect len(MemberList)=%d, got %d", expectedMemberCount, len(membersResp.Members))
diff --git a/tests/integration/metrics_test.go b/tests/integration/metrics_test.go
index 8212bb773eb1..29e04f412f25 100644
--- a/tests/integration/metrics_test.go
+++ b/tests/integration/metrics_test.go
@@ -15,7 +15,6 @@
package integration
import (
- "context"
"fmt"
"net/http"
"strconv"
@@ -40,9 +39,7 @@ func TestMetricDbSizeBoot(t *testing.T) {
v, err := clus.Members[0].Metric("etcd_debugging_mvcc_db_total_size_in_bytes")
require.NoError(t, err)
- if v == "0" {
- t.Fatalf("expected non-zero, got %q", v)
- }
+ require.NotEqualf(t, "0", v, "expected non-zero, got %q", v)
}
func TestMetricDbSizeDefrag(t *testing.T) {
@@ -63,7 +60,7 @@ func testMetricDbSizeDefrag(t *testing.T, name string) {
putreq := &pb.PutRequest{Key: []byte("k"), Value: make([]byte, 4096)}
for i := 0; i < numPuts; i++ {
time.Sleep(10 * time.Millisecond) // to execute multiple backend txn
- _, err := kvc.Put(context.TODO(), putreq)
+ _, err := kvc.Put(t.Context(), putreq)
require.NoError(t, err)
}
@@ -75,25 +72,21 @@ func testMetricDbSizeDefrag(t *testing.T, name string) {
require.NoError(t, err)
bv, err := strconv.Atoi(beforeDefrag)
require.NoError(t, err)
- if bv < expected {
- t.Fatalf("expected db size greater than %d, got %d", expected, bv)
- }
+ require.GreaterOrEqualf(t, bv, expected, "expected db size greater than %d, got %d", expected, bv)
beforeDefragInUse, err := clus.Members[0].Metric("etcd_mvcc_db_total_size_in_use_in_bytes")
require.NoError(t, err)
biu, err := strconv.Atoi(beforeDefragInUse)
require.NoError(t, err)
- if biu < expected {
- t.Fatalf("expected db size in use is greater than %d, got %d", expected, biu)
- }
+ require.GreaterOrEqualf(t, biu, expected, "expected db size in use is greater than %d, got %d", expected, biu)
// clear out historical keys, in use bytes should free pages
creq := &pb.CompactionRequest{Revision: int64(numPuts), Physical: true}
- _, kerr := kvc.Compact(context.TODO(), creq)
+ _, kerr := kvc.Compact(t.Context(), creq)
require.NoError(t, kerr)
validateAfterCompactionInUse := func() error {
// Put to move PendingPages to FreePages
- _, verr := kvc.Put(context.TODO(), putreq)
+ _, verr := kvc.Put(t.Context(), putreq)
require.NoError(t, verr)
time.Sleep(500 * time.Millisecond)
@@ -116,29 +109,23 @@ func testMetricDbSizeDefrag(t *testing.T, name string) {
break
}
retry++
- if retry >= maxRetry {
- t.Fatalf("%v", err.Error())
- }
+ require.Lessf(t, retry, maxRetry, "%v", err.Error())
}
// defrag should give freed space back to fs
- mc.Defragment(context.TODO(), &pb.DefragmentRequest{})
+ mc.Defragment(t.Context(), &pb.DefragmentRequest{})
afterDefrag, err := clus.Members[0].Metric(name + "_mvcc_db_total_size_in_bytes")
require.NoError(t, err)
av, err := strconv.Atoi(afterDefrag)
require.NoError(t, err)
- if bv <= av {
- t.Fatalf("expected less than %d, got %d after defrag", bv, av)
- }
+ require.Greaterf(t, bv, av, "expected less than %d, got %d after defrag", bv, av)
afterDefragInUse, err := clus.Members[0].Metric("etcd_mvcc_db_total_size_in_use_in_bytes")
require.NoError(t, err)
adiu, err := strconv.Atoi(afterDefragInUse)
require.NoError(t, err)
- if adiu > av {
- t.Fatalf("db size in use (%d) is expected less than db size (%d) after defrag", adiu, av)
- }
+ require.LessOrEqualf(t, adiu, av, "db size in use (%d) is expected less than db size (%d) after defrag", adiu, av)
}
func TestMetricQuotaBackendBytes(t *testing.T) {
@@ -150,9 +137,7 @@ func TestMetricQuotaBackendBytes(t *testing.T) {
require.NoError(t, err)
qv, err := strconv.ParseFloat(qs, 64)
require.NoError(t, err)
- if int64(qv) != storage.DefaultQuotaBytes {
- t.Fatalf("expected %d, got %f", storage.DefaultQuotaBytes, qv)
- }
+ require.Equalf(t, storage.DefaultQuotaBytes, int64(qv), "expected %d, got %f", storage.DefaultQuotaBytes, qv)
}
func TestMetricsHealth(t *testing.T) {
@@ -174,9 +159,7 @@ func TestMetricsHealth(t *testing.T) {
hv, err := clus.Members[0].Metric("etcd_server_health_failures")
require.NoError(t, err)
- if hv != "0" {
- t.Fatalf("expected '0' from etcd_server_health_failures, got %q", hv)
- }
+ require.Equalf(t, "0", hv, "expected '0' from etcd_server_health_failures, got %q", hv)
}
func TestMetricsRangeDurationSeconds(t *testing.T) {
@@ -190,11 +173,11 @@ func TestMetricsRangeDurationSeconds(t *testing.T) {
"my-namespace/foobar", "my-namespace/foobar1", "namespace/foobar1",
}
for _, key := range keys {
- _, err := client.Put(context.Background(), key, "data")
+ _, err := client.Put(t.Context(), key, "data")
require.NoError(t, err)
}
- _, err := client.Get(context.Background(), "", clientv3.WithFromKey())
+ _, err := client.Get(t.Context(), "", clientv3.WithFromKey())
require.NoError(t, err)
rangeDurationSeconds, err := clus.Members[0].Metric("etcd_server_range_duration_seconds")
diff --git a/tests/integration/network_partition_test.go b/tests/integration/network_partition_test.go
index 059e93762667..8c24fce2f27a 100644
--- a/tests/integration/network_partition_test.go
+++ b/tests/integration/network_partition_test.go
@@ -19,6 +19,8 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"go.etcd.io/etcd/tests/v3/framework/integration"
)
@@ -65,9 +67,7 @@ func TestNetworkPartition5MembersLeaderInMajority(t *testing.T) {
}
t.Logf("[%d] got %v", i, err)
}
- if err != nil {
- t.Fatalf("failed after 3 tries (%v)", err)
- }
+ require.NoErrorf(t, err, "failed after 3 tries (%v)", err)
}
func testNetworkPartition5MembersLeaderInMajority(t *testing.T) error {
diff --git a/tests/integration/proxy/grpcproxy/cluster_test.go b/tests/integration/proxy/grpcproxy/cluster_test.go
index ca2fcb506b37..3d398b75a438 100644
--- a/tests/integration/proxy/grpcproxy/cluster_test.go
+++ b/tests/integration/proxy/grpcproxy/cluster_test.go
@@ -15,7 +15,6 @@
package grpcproxy
import (
- "context"
"net"
"os"
"testing"
@@ -31,13 +30,13 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints"
"go.etcd.io/etcd/server/v3/proxy/grpcproxy"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestClusterProxyMemberList(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
lg := zaptest.NewLogger(t)
@@ -51,27 +50,19 @@ func TestClusterProxyMemberList(t *testing.T) {
Endpoints: []string{cts.caddr},
DialTimeout: 5 * time.Second,
}
- client, err := integration2.NewClient(t, cfg)
- if err != nil {
- t.Fatalf("err %v, want nil", err)
- }
+ client, err := integration.NewClient(t, cfg)
+ require.NoErrorf(t, err, "err %v, want nil", err)
defer client.Close()
// wait some time for register-loop to write keys
time.Sleep(200 * time.Millisecond)
var mresp *clientv3.MemberListResponse
- mresp, err = client.Cluster.MemberList(context.Background())
- if err != nil {
- t.Fatalf("err %v, want nil", err)
- }
+ mresp, err = client.Cluster.MemberList(t.Context())
+ require.NoErrorf(t, err, "err %v, want nil", err)
- if len(mresp.Members) != 1 {
- t.Fatalf("len(mresp.Members) expected 1, got %d (%+v)", len(mresp.Members), mresp.Members)
- }
- if len(mresp.Members[0].ClientURLs) != 1 {
- t.Fatalf("len(mresp.Members[0].ClientURLs) expected 1, got %d (%+v)", len(mresp.Members[0].ClientURLs), mresp.Members[0].ClientURLs[0])
- }
+ require.Lenf(t, mresp.Members, 1, "len(mresp.Members) expected 1, got %d (%+v)", len(mresp.Members), mresp.Members)
+ require.Lenf(t, mresp.Members[0].ClientURLs, 1, "len(mresp.Members[0].ClientURLs) expected 1, got %d (%+v)", len(mresp.Members[0].ClientURLs), mresp.Members[0].ClientURLs[0])
assert.Contains(t, mresp.Members, &pb.Member{Name: hostname, ClientURLs: []string{cts.caddr}})
// test proxy member add
@@ -81,13 +72,9 @@ func TestClusterProxyMemberList(t *testing.T) {
time.Sleep(200 * time.Millisecond)
// check add member succ
- mresp, err = client.Cluster.MemberList(context.Background())
- if err != nil {
- t.Fatalf("err %v, want nil", err)
- }
- if len(mresp.Members) != 2 {
- t.Fatalf("len(mresp.Members) expected 2, got %d (%+v)", len(mresp.Members), mresp.Members)
- }
+ mresp, err = client.Cluster.MemberList(t.Context())
+ require.NoErrorf(t, err, "err %v, want nil", err)
+ require.Lenf(t, mresp.Members, 2, "len(mresp.Members) expected 2, got %d (%+v)", len(mresp.Members), mresp.Members)
assert.Contains(t, mresp.Members, &pb.Member{Name: hostname, ClientURLs: []string{newMemberAddr}})
// test proxy member delete
@@ -96,13 +83,9 @@ func TestClusterProxyMemberList(t *testing.T) {
time.Sleep(200 * time.Millisecond)
// check delete member succ
- mresp, err = client.Cluster.MemberList(context.Background())
- if err != nil {
- t.Fatalf("err %v, want nil", err)
- }
- if len(mresp.Members) != 1 {
- t.Fatalf("len(mresp.Members) expected 1, got %d (%+v)", len(mresp.Members), mresp.Members)
- }
+ mresp, err = client.Cluster.MemberList(t.Context())
+ require.NoErrorf(t, err, "err %v, want nil", err)
+ require.Lenf(t, mresp.Members, 1, "len(mresp.Members) expected 1, got %d (%+v)", len(mresp.Members), mresp.Members)
assert.Contains(t, mresp.Members, &pb.Member{Name: hostname, ClientURLs: []string{cts.caddr}})
}
@@ -132,7 +115,7 @@ func newClusterProxyServer(lg *zap.Logger, endpoints []string, prefix string, t
Endpoints: endpoints,
DialTimeout: 5 * time.Second,
}
- client, err := integration2.NewClient(t, cfg)
+ client, err := integration.NewClient(t, cfg)
require.NoError(t, err)
cts := &clusterproxyTestServer{
@@ -162,10 +145,7 @@ func newClusterProxyServer(lg *zap.Logger, endpoints []string, prefix string, t
func deregisterMember(c *clientv3.Client, prefix, addr string, t *testing.T) {
em, err := endpoints.NewManager(c, prefix)
- if err != nil {
- t.Fatalf("new endpoint manager failed, err %v", err)
- }
- if err = em.DeleteEndpoint(c.Ctx(), prefix+"/"+addr); err != nil {
- t.Fatalf("delete endpoint failed, err %v", err)
- }
+ require.NoErrorf(t, err, "new endpoint manager failed, err")
+ err = em.DeleteEndpoint(c.Ctx(), prefix+"/"+addr)
+ require.NoErrorf(t, err, "delete endpoint failed, err")
}
diff --git a/tests/integration/proxy/grpcproxy/kv_test.go b/tests/integration/proxy/grpcproxy/kv_test.go
index b871eca95ea8..9748de9d3279 100644
--- a/tests/integration/proxy/grpcproxy/kv_test.go
+++ b/tests/integration/proxy/grpcproxy/kv_test.go
@@ -15,7 +15,6 @@
package grpcproxy
import (
- "context"
"net"
"testing"
"time"
@@ -26,13 +25,13 @@ import (
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/proxy/grpcproxy"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestKVProxyRange(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
kvts := newKVProxyServer([]string{clus.Members[0].GRPCURL}, t)
@@ -43,14 +42,10 @@ func TestKVProxyRange(t *testing.T) {
Endpoints: []string{kvts.l.Addr().String()},
DialTimeout: 5 * time.Second,
}
- client, err := integration2.NewClient(t, cfg)
- if err != nil {
- t.Fatalf("err = %v, want nil", err)
- }
- _, err = client.Get(context.Background(), "foo")
- if err != nil {
- t.Fatalf("err = %v, want nil", err)
- }
+ client, err := integration.NewClient(t, cfg)
+ require.NoErrorf(t, err, "err = %v, want nil", err)
+ _, err = client.Get(t.Context(), "foo")
+ require.NoErrorf(t, err, "err = %v, want nil", err)
client.Close()
}
@@ -72,7 +67,7 @@ func newKVProxyServer(endpoints []string, t *testing.T) *kvproxyTestServer {
Endpoints: endpoints,
DialTimeout: 5 * time.Second,
}
- client, err := integration2.NewClient(t, cfg)
+ client, err := integration.NewClient(t, cfg)
require.NoError(t, err)
kvp, _ := grpcproxy.NewKvProxy(client)
diff --git a/tests/integration/proxy/grpcproxy/register_test.go b/tests/integration/proxy/grpcproxy/register_test.go
index a0fb5272c52d..1ea252e16a59 100644
--- a/tests/integration/proxy/grpcproxy/register_test.go
+++ b/tests/integration/proxy/grpcproxy/register_test.go
@@ -18,18 +18,19 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints"
"go.etcd.io/etcd/server/v3/proxy/grpcproxy"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestRegister(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
cli := clus.Client(0)
paddr := clus.Members[0].GRPCURL
@@ -40,12 +41,8 @@ func TestRegister(t *testing.T) {
donec := grpcproxy.Register(zaptest.NewLogger(t), cli, testPrefix, paddr, 5)
ups := <-wa
- if len(ups) != 1 {
- t.Fatalf("len(ups) expected 1, got %d (%v)", len(ups), ups)
- }
- if ups[0].Endpoint.Addr != paddr {
- t.Fatalf("ups[0].Addr expected %q, got %q", paddr, ups[0].Endpoint.Addr)
- }
+ require.Lenf(t, ups, 1, "len(ups) expected 1, got %d (%v)", len(ups), ups)
+ require.Equalf(t, ups[0].Endpoint.Addr, paddr, "ups[0].Addr expected %q, got %q", paddr, ups[0].Endpoint.Addr)
cli.Close()
clus.TakeClient(0)
@@ -58,12 +55,8 @@ func TestRegister(t *testing.T) {
func mustCreateWatcher(t *testing.T, c *clientv3.Client, prefix string) endpoints.WatchChannel {
em, err := endpoints.NewManager(c, prefix)
- if err != nil {
- t.Fatalf("failed to create endpoints.Manager: %v", err)
- }
+ require.NoErrorf(t, err, "failed to create endpoints.Manager")
wc, err := em.NewWatchChannel(c.Ctx())
- if err != nil {
- t.Fatalf("failed to resolve %q (%v)", prefix, err)
- }
+ require.NoErrorf(t, err, "failed to resolve %q", prefix)
return wc
}
diff --git a/tests/integration/revision_test.go b/tests/integration/revision_test.go
index fd53cfb37b41..6d40adc587ac 100644
--- a/tests/integration/revision_test.go
+++ b/tests/integration/revision_test.go
@@ -81,7 +81,7 @@ func testRevisionMonotonicWithFailures(t *testing.T, testDuration time.Duration,
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, UseBridge: true})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), testDuration)
+ ctx, cancel := context.WithTimeout(t.Context(), testDuration)
defer cancel()
wg := sync.WaitGroup{}
@@ -104,7 +104,7 @@ func testRevisionMonotonicWithFailures(t *testing.T, testDuration time.Duration,
injectFailures(clus)
wg.Wait()
kv := clus.Client(0)
- resp, err := kv.Get(context.Background(), "foo")
+ resp, err := kv.Get(t.Context(), "foo")
require.NoError(t, err)
t.Logf("Revision %d", resp.Header.Revision)
}
@@ -132,9 +132,7 @@ func getWorker(ctx context.Context, t *testing.T, clus *integration.Cluster) {
if resp == nil {
continue
}
- if prevRev > resp.Header.Revision {
- t.Fatalf("rev is less than previously observed revision, rev: %d, prevRev: %d", resp.Header.Revision, prevRev)
- }
+ require.LessOrEqualf(t, prevRev, resp.Header.Revision, "rev is less than previously observed revision, rev: %d, prevRev: %d", resp.Header.Revision, prevRev)
prevRev = resp.Header.Revision
}
}
diff --git a/tests/integration/snapshot/member_test.go b/tests/integration/snapshot/member_test.go
index be419efdc27c..de4baec62c0c 100644
--- a/tests/integration/snapshot/member_test.go
+++ b/tests/integration/snapshot/member_test.go
@@ -26,14 +26,14 @@ import (
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/server/v3/embed"
"go.etcd.io/etcd/server/v3/etcdserver"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// TestSnapshotV3RestoreMultiMemberAdd ensures that multiple members
// can boot into the same cluster after being restored from a same
// snapshot file, and also be able to add another member to the cluster.
func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
kvs := []kv{{"foo1", "bar1"}, {"foo2", "bar2"}, {"foo3", "bar3"}}
dbPath := createSnapshotFile(t, kvs)
@@ -50,19 +50,19 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
// wait for health interval + leader election
time.Sleep(etcdserver.HealthInterval + 2*time.Second)
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{cURLs[0].String()}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{cURLs[0].String()}})
require.NoError(t, err)
defer cli.Close()
urls := newEmbedURLs(t, 2)
newCURLs, newPURLs := urls[:1], urls[1:]
- _, err = cli.MemberAdd(context.Background(), []string{newPURLs[0].String()})
+ _, err = cli.MemberAdd(t.Context(), []string{newPURLs[0].String()})
require.NoError(t, err)
// wait for membership reconfiguration apply
time.Sleep(testutil.ApplyTimeout)
- cfg := integration2.NewEmbedConfig(t, "3")
+ cfg := integration.NewEmbedConfig(t, "3")
cfg.InitialClusterToken = testClusterTkn
cfg.ClusterState = "existing"
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = newCURLs, newCURLs
@@ -85,30 +85,24 @@ func TestSnapshotV3RestoreMultiMemberAdd(t *testing.T) {
t.Fatalf("failed to start the newly added etcd member")
}
- cli2, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{newCURLs[0].String()}})
+ cli2, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{newCURLs[0].String()}})
require.NoError(t, err)
defer cli2.Close()
- ctx, cancel := context.WithTimeout(context.Background(), testutil.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), testutil.RequestTimeout)
mresp, err := cli2.MemberList(ctx)
cancel()
require.NoError(t, err)
- if len(mresp.Members) != 4 {
- t.Fatalf("expected 4 members, got %+v", mresp)
- }
+ require.Lenf(t, mresp.Members, 4, "expected 4 members, got %+v", mresp)
// make sure restored cluster has kept all data on recovery
var gresp *clientv3.GetResponse
- ctx, cancel = context.WithTimeout(context.Background(), testutil.RequestTimeout)
+ ctx, cancel = context.WithTimeout(t.Context(), testutil.RequestTimeout)
gresp, err = cli2.Get(ctx, "foo", clientv3.WithPrefix())
cancel()
require.NoError(t, err)
for i := range gresp.Kvs {
- if string(gresp.Kvs[i].Key) != kvs[i].k {
- t.Fatalf("#%d: key expected %s, got %s", i, kvs[i].k, gresp.Kvs[i].Key)
- }
- if string(gresp.Kvs[i].Value) != kvs[i].v {
- t.Fatalf("#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[i].Value)
- }
+ require.Equalf(t, string(gresp.Kvs[i].Key), kvs[i].k, "#%d: key expected %s, got %s", i, kvs[i].k, gresp.Kvs[i].Key)
+ require.Equalf(t, string(gresp.Kvs[i].Value), kvs[i].v, "#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[i].Value)
}
}
diff --git a/tests/integration/snapshot/v3_snapshot_test.go b/tests/integration/snapshot/v3_snapshot_test.go
index 28b9e1a7a651..4ceba94d5949 100644
--- a/tests/integration/snapshot/v3_snapshot_test.go
+++ b/tests/integration/snapshot/v3_snapshot_test.go
@@ -34,14 +34,14 @@ import (
"go.etcd.io/etcd/etcdutl/v3/snapshot"
"go.etcd.io/etcd/pkg/v3/cpuutil"
"go.etcd.io/etcd/server/v3/embed"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
// TestSnapshotV3RestoreSingle tests single node cluster restoring
// from a snapshot file.
func TestSnapshotV3RestoreSingle(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
kvs := []kv{{"foo1", "bar1"}, {"foo2", "bar2"}, {"foo3", "bar3"}}
dbPath := createSnapshotFile(t, kvs)
@@ -49,7 +49,7 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
urls := newEmbedURLs(t, clusterN*2)
cURLs, pURLs := urls[:clusterN], urls[clusterN:]
- cfg := integration2.NewEmbedConfig(t, "s1")
+ cfg := integration.NewEmbedConfig(t, "s1")
cfg.InitialClusterToken = testClusterTkn
cfg.ClusterState = "existing"
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = cURLs, cURLs
@@ -83,16 +83,14 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
}
var cli *clientv3.Client
- cli, err = integration2.NewClient(t, clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}})
+ cli, err = integration.NewClient(t, clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}})
require.NoError(t, err)
defer cli.Close()
for i := range kvs {
var gresp *clientv3.GetResponse
- gresp, err = cli.Get(context.Background(), kvs[i].k)
+ gresp, err = cli.Get(t.Context(), kvs[i].k)
require.NoError(t, err)
- if string(gresp.Kvs[0].Value) != kvs[i].v {
- t.Fatalf("#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[0].Value)
- }
+ require.Equalf(t, string(gresp.Kvs[0].Value), kvs[i].v, "#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[0].Value)
}
}
@@ -100,7 +98,7 @@ func TestSnapshotV3RestoreSingle(t *testing.T) {
// can boot into the same cluster after being restored from a same
// snapshot file.
func TestSnapshotV3RestoreMulti(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
kvs := []kv{{"foo1", "bar1"}, {"foo2", "bar2"}, {"foo3", "bar3"}}
dbPath := createSnapshotFile(t, kvs)
@@ -116,16 +114,14 @@ func TestSnapshotV3RestoreMulti(t *testing.T) {
time.Sleep(time.Second)
for i := 0; i < clusterN; i++ {
- cli, err := integration2.NewClient(t, clientv3.Config{Endpoints: []string{cURLs[i].String()}})
+ cli, err := integration.NewClient(t, clientv3.Config{Endpoints: []string{cURLs[i].String()}})
require.NoError(t, err)
defer cli.Close()
for i := range kvs {
var gresp *clientv3.GetResponse
- gresp, err = cli.Get(context.Background(), kvs[i].k)
+ gresp, err = cli.Get(t.Context(), kvs[i].k)
require.NoError(t, err)
- if string(gresp.Kvs[0].Value) != kvs[i].v {
- t.Fatalf("#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[0].Value)
- }
+ require.Equalf(t, string(gresp.Kvs[0].Value), kvs[i].v, "#%d: value expected %s, got %s", i, kvs[i].v, gresp.Kvs[0].Value)
}
}
}
@@ -136,13 +132,12 @@ func TestCorruptedBackupFileCheck(t *testing.T) {
t.Skipf("skipping on big endian platforms since testdata/corrupted_backup.db is in little endian format")
}
dbPath := testutils.MustAbsPath("testdata/corrupted_backup.db")
- integration2.BeforeTest(t)
- if _, err := os.Stat(dbPath); err != nil {
- t.Fatalf("test file [%s] does not exist: %v", dbPath, err)
- }
+ integration.BeforeTest(t)
+ _, err := os.Stat(dbPath)
+ require.NoErrorf(t, err, "test file [%s] does not exist: %v", dbPath, err)
sp := snapshot.NewV3(zaptest.NewLogger(t))
- _, err := sp.Status(dbPath)
+ _, err = sp.Status(dbPath)
expectedErrKeywords := "snapshot file integrity check failed"
/* example error message:
snapshot file integrity check failed. 2 errors found.
@@ -171,7 +166,7 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
urls := newEmbedURLs(t, clusterN*2)
cURLs, pURLs := urls[:clusterN], urls[clusterN:]
- cfg := integration2.NewEmbedConfig(t, "default")
+ cfg := integration.NewEmbedConfig(t, "default")
cfg.ClusterState = "new"
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = cURLs, cURLs
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = pURLs, pURLs
@@ -188,11 +183,11 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
}
ccfg := clientv3.Config{Endpoints: []string{cfg.AdvertiseClientUrls[0].String()}}
- cli, err := integration2.NewClient(t, ccfg)
+ cli, err := integration.NewClient(t, ccfg)
require.NoError(t, err)
defer cli.Close()
for i := range kvs {
- ctx, cancel := context.WithTimeout(context.Background(), testutil.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), testutil.RequestTimeout)
_, err = cli.Put(ctx, kvs[i].k, kvs[i].v)
cancel()
require.NoError(t, err)
@@ -200,7 +195,7 @@ func createSnapshotFile(t *testing.T, kvs []kv) string {
sp := snapshot.NewV3(zaptest.NewLogger(t))
dpPath := filepath.Join(t.TempDir(), fmt.Sprintf("snapshot%d.db", time.Now().Nanosecond()))
- _, err = sp.Save(context.Background(), ccfg, dpPath)
+ _, err = sp.Save(t.Context(), ccfg, dpPath)
require.NoError(t, err)
return dpPath
}
@@ -223,7 +218,7 @@ func restoreCluster(t *testing.T, clusterN int, dbPath string) (
cfgs := make([]*embed.Config, clusterN)
for i := 0; i < clusterN; i++ {
- cfg := integration2.NewEmbedConfig(t, fmt.Sprintf("m%d", i))
+ cfg := integration.NewEmbedConfig(t, fmt.Sprintf("m%d", i))
cfg.InitialClusterToken = testClusterTkn
cfg.ClusterState = "existing"
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = []url.URL{cURLs[i]}, []url.URL{cURLs[i]}
@@ -275,7 +270,7 @@ func restoreCluster(t *testing.T, clusterN int, dbPath string) (
func newEmbedURLs(t testutil.TB, n int) (urls []url.URL) {
urls = make([]url.URL, n)
for i := 0; i < n; i++ {
- l := integration2.NewLocalListener(t)
+ l := integration.NewLocalListener(t)
defer l.Close()
u, err := url.Parse(fmt.Sprintf("unix://%s", l.Addr()))
diff --git a/tests/integration/testing_test.go b/tests/integration/testing_test.go
index f49e58d83d77..3f7e4229c7f2 100644
--- a/tests/integration/testing_test.go
+++ b/tests/integration/testing_test.go
@@ -17,11 +17,11 @@ package integration_test
import (
"testing"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
func TestBeforeTestWithoutLeakDetection(t *testing.T) {
- integration2.BeforeTest(t, integration2.WithoutGoLeakDetection(), integration2.WithoutSkipInShort())
+ integration.BeforeTest(t, integration.WithoutGoLeakDetection(), integration.WithoutSkipInShort())
// Intentional leak that should get ignored
go func() {
}()
diff --git a/tests/integration/tracing_test.go b/tests/integration/tracing_test.go
index 5ecfc12a5b9d..b91b248ed1b9 100644
--- a/tests/integration/tracing_test.go
+++ b/tests/integration/tracing_test.go
@@ -16,17 +16,22 @@ package integration
import (
"context"
+ "fmt"
"net"
+ "strings"
"testing"
"time"
+ "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
- "go.opentelemetry.io/otel/trace"
traceservice "go.opentelemetry.io/proto/otlp/collector/trace/v1"
+ commonv1 "go.opentelemetry.io/proto/otlp/common/v1"
+ v1 "go.opentelemetry.io/proto/otlp/trace/v1"
"google.golang.org/grpc"
+ "google.golang.org/protobuf/testing/protocmp"
"go.etcd.io/etcd/client/pkg/v3/testutil"
clientv3 "go.etcd.io/etcd/client/v3"
@@ -39,47 +44,228 @@ func TestTracing(t *testing.T) {
testutil.SkipTestIfShortMode(t,
"Wal creation tests are depending on embedded etcd server so are integration-level tests.")
- // Test Unary RPC tracing
- t.Run("UnaryRPC", func(t *testing.T) {
- testRPCTracing(t, "UnaryRPC", containsUnaryRPCSpan, func(cli *clientv3.Client) error {
- // make a request with the instrumented client
- resp, err := cli.Get(context.TODO(), "key")
- require.NoError(t, err)
- require.Empty(t, resp.Kvs)
- return nil
- })
- })
+ for _, tc := range []struct {
+ name string
+ rpc func(context.Context, *clientv3.Client) error
+ wantSpan *v1.Span
+ }{
+ {
+ name: "UnaryGet",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.Get(ctx, "key")
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "etcdserverpb.KV/Range",
+ // Attributes are set outside Etcd in otelgrpc, so they are ignored here.
+ },
+ },
+ {
+ name: "UnaryGetWithCountOnly",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.Get(ctx, "key", clientv3.WithCountOnly())
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "range",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "range_begin",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: "key"}},
+ },
+ {
+ Key: "range_end",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: ""}},
+ },
+ {
+ Key: "rev",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 0}},
+ },
+ {
+ Key: "limit",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 0}},
+ },
+ {
+ Key: "count_only",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: true}},
+ },
+ {
+ Key: "keys_only",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: false}},
+ },
+ },
+ },
+ },
+ {
+ name: "UnaryTxn",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.Txn(ctx).
+ If(clientv3.Compare(clientv3.ModRevision("cmp_key"), "=", 1)).
+ Then(clientv3.OpPut("op_key", "val", clientv3.WithLease(1234)), clientv3.OpGet("other_key")).
+ Commit()
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "txn",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "compare_first_key",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: "cmp_key"}},
+ },
+ {
+ Key: "success_first_key",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: "op_key"}},
+ },
+ {
+ Key: "success_first_type",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: "put"}},
+ },
+ {
+ Key: "success_first_lease",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 1234}},
+ },
+ {
+ Key: "compare_len",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 1}},
+ },
+ {
+ Key: "success_len",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 2}},
+ },
+ {
+ Key: "failure_len",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 0}},
+ },
+ {
+ Key: "read_only",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: false}},
+ },
+ },
+ },
+ },
+ {
+ name: "UnaryLeaseGrant",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.Grant(ctx, 1_000_123)
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "lease_grant",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "id",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 0}},
+ },
+ {
+ Key: "ttl",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 1_000_123}},
+ },
+ },
+ },
+ },
+ {
+ name: "UnaryLeaseRenew",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.KeepAliveOnce(ctx, 2345)
+ if err != nil && strings.Contains(err.Error(), "requested lease not found") {
+ // errors.Is does not work across gRPC bounduaries.
+ return nil
+ }
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "lease_renew",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "id",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 2345}},
+ },
+ },
+ },
+ },
+ {
+ name: "UnaryLeaseRevoke",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ _, err := cli.Revoke(ctx, 1234)
+ if err != nil && strings.Contains(err.Error(), "requested lease not found") {
+ // errors.Is does not work across gRPC bounduaries.
+ return nil
+ }
+ return err
+ },
+ wantSpan: &v1.Span{
+ Name: "lease_revoke",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "id",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 1234}},
+ },
+ },
+ },
+ },
+ {
+ name: "StreamWatch",
+ rpc: func(ctx context.Context, cli *clientv3.Client) error {
+ // Create a context with a reasonable timeout
+ ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
+ defer cancel()
- // Test Stream RPC tracing
- t.Run("StreamRPC", func(t *testing.T) {
- testRPCTracing(t, "StreamRPC", containsStreamRPCSpan, func(cli *clientv3.Client) error {
- // Create a context with a reasonable timeout
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- // Create a watch channel
- watchChan := cli.Watch(ctx, "watch-key")
-
- // Put a value to trigger the watch
- _, err := cli.Put(context.TODO(), "watch-key", "watch-value")
- require.NoError(t, err)
-
- // Wait for watch event
- select {
- case watchResp := <-watchChan:
- require.NoError(t, watchResp.Err())
- require.Len(t, watchResp.Events, 1)
- t.Log("Received watch event successfully")
- case <-time.After(5 * time.Second):
- t.Fatal("Timed out waiting for watch event")
- }
- return nil
+ // Create a watch channel
+ watchChan := cli.Watch(ctx, "watch-key", clientv3.WithProgressNotify(), clientv3.WithRev(1))
+
+ // Put a value to trigger the watch
+ _, err := cli.Put(ctx, "watch-key", "watch-value")
+ if err != nil {
+ return err
+ }
+
+ // Wait for watch event
+ select {
+ case watchResp := <-watchChan:
+ return watchResp.Err()
+ case <-time.After(5 * time.Second):
+ return fmt.Errorf("Timed out waiting for watch event")
+ }
+ },
+ wantSpan: &v1.Span{
+ Name: "watch",
+ Attributes: []*commonv1.KeyValue{
+ {
+ Key: "key",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: "watch-key"}},
+ },
+ {
+ Key: "range_end",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_StringValue{StringValue: ""}},
+ },
+ {
+ Key: "start_rev",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_IntValue{IntValue: 1}},
+ },
+ {
+ Key: "progress_notify",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: true}},
+ },
+ {
+ Key: "prev_kv",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: false}},
+ },
+ {
+ Key: "fragment",
+ Value: &commonv1.AnyValue{Value: &commonv1.AnyValue_BoolValue{BoolValue: false}},
+ },
+ },
+ },
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ testRPCTracing(t, tc.wantSpan, tc.rpc)
})
- })
+ }
}
// testRPCTracing is a common test function for both Unary and Stream RPC tracing
-func testRPCTracing(t *testing.T, testName string, filterFunc func(*traceservice.ExportTraceServiceRequest) bool, clientAction func(*clientv3.Client) error) {
+func testRPCTracing(t *testing.T, wantSpan *v1.Span, clientAction func(context.Context, *clientv3.Client) error) {
// set up trace collector
listener, err := net.Listen("tcp", "localhost:")
require.NoError(t, err)
@@ -90,7 +276,49 @@ func testRPCTracing(t *testing.T, testName string, filterFunc func(*traceservice
srv := grpc.NewServer()
traceservice.RegisterTraceServiceServer(srv, &traceServer{
traceFound: traceFound,
- filterFunc: filterFunc,
+ filterFunc: func(req *traceservice.ExportTraceServiceRequest) bool {
+ for _, resourceSpans := range req.GetResourceSpans() {
+ // Skip spans which weren't produced by test's gRPC client.
+ matched := false
+ for _, attr := range resourceSpans.GetResource().GetAttributes() {
+ if attr.GetKey() == "service.name" && attr.GetValue().GetStringValue() == "integration-test-tracing" {
+ matched = true
+ break
+ }
+ }
+ if !matched {
+ continue
+ }
+
+ for _, scoped := range resourceSpans.GetScopeSpans() {
+ for _, gotSpan := range scoped.GetSpans() {
+ if gotSpan.GetName() != wantSpan.GetName() {
+ continue
+ }
+ if len(wantSpan.GetAttributes()) == 0 {
+ // Diff will compare only attributes and events when needed
+ return true
+ }
+ if gotSpan.GetName() == "lease_grant" {
+ // Ignore ID in lease grant which is not controlled by the client.
+ for _, attr := range gotSpan.GetAttributes() {
+ if attr.GetKey() == "id" {
+ attr.Value.Value.(*commonv1.AnyValue_IntValue).IntValue = 0
+ }
+ }
+ }
+ if diff := cmp.Diff(wantSpan, gotSpan,
+ protocmp.Transform(),
+ protocmp.IgnoreFields(&v1.Span{}, "end_time_unix_nano", "flags", "kind", "parent_span_id", "span_id", "start_time_unix_nano", "status", "trace_id", "events"),
+ ); diff != "" {
+ t.Errorf("Span mismatch (-want +got):\n%s", diff)
+ }
+ return true
+ }
+ }
+ }
+ return false
+ },
})
go srv.Serve(listener)
@@ -101,7 +329,7 @@ func testRPCTracing(t *testing.T, testName string, filterFunc func(*traceservice
cfg.EnableDistributedTracing = true
cfg.DistributedTracingAddress = listener.Addr().String()
cfg.DistributedTracingServiceName = "integration-test-tracing"
- cfg.DistributedTracingSamplingRatePerMillion = 100
+ cfg.DistributedTracingSamplingRatePerMillion = 100 // overridden later in the test
// start an etcd instance with tracing enabled
etcdSrv, err := embed.StartEtcd(cfg)
@@ -117,9 +345,8 @@ func testRPCTracing(t *testing.T, testName string, filterFunc func(*traceservice
}
// create a client that has tracing enabled
- tracer := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
- defer tracer.Shutdown(context.TODO())
- tp := trace.TracerProvider(tracer)
+ tp := sdktrace.NewTracerProvider()
+ defer tp.Shutdown(t.Context())
tracingOpts := []otelgrpc.Option{
otelgrpc.WithTracerProvider(tp),
@@ -142,51 +369,20 @@ func testRPCTracing(t *testing.T, testName string, filterFunc func(*traceservice
defer cli.Close()
// Execute the client action (either Unary or Stream RPC)
- err = clientAction(cli)
+ err = clientAction(t.Context(), cli)
require.NoError(t, err)
// Wait for a span to be recorded from our request
select {
case <-traceFound:
- t.Logf("%s trace found", testName)
+ t.Logf("Trace found")
return
case <-time.After(30 * time.Second):
+ // default exporter has 5s scheduling delay
t.Fatal("Timed out waiting for trace")
}
}
-func containsUnaryRPCSpan(req *traceservice.ExportTraceServiceRequest) bool {
- for _, resourceSpans := range req.GetResourceSpans() {
- for _, attr := range resourceSpans.GetResource().GetAttributes() {
- if attr.GetKey() != "service.name" && attr.GetValue().GetStringValue() != "integration-test-tracing" {
- continue
- }
- for _, scoped := range resourceSpans.GetScopeSpans() {
- for _, span := range scoped.GetSpans() {
- if span.GetName() == "etcdserverpb.KV/Range" {
- return true
- }
- }
- }
- }
- }
- return false
-}
-
-// containsStreamRPCSpan checks for Watch/Watch spans in trace data
-func containsStreamRPCSpan(req *traceservice.ExportTraceServiceRequest) bool {
- for _, resourceSpans := range req.GetResourceSpans() {
- for _, scoped := range resourceSpans.GetScopeSpans() {
- for _, span := range scoped.GetSpans() {
- if span.GetName() == "etcdserverpb.Watch/Watch" {
- return true
- }
- }
- }
- }
- return false
-}
-
// traceServer implements TracesServiceServer
type traceServer struct {
traceFound chan struct{}
diff --git a/tests/integration/txn_range_consistency_test.go b/tests/integration/txn_range_consistency_test.go
new file mode 100644
index 000000000000..e63df6e1b2e3
--- /dev/null
+++ b/tests/integration/txn_range_consistency_test.go
@@ -0,0 +1,67 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 integration
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
+)
+
+func TestTxnRangeRequestConsistency(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
+ defer clus.Terminate(t)
+
+ kvc := integration.ToGRPC(clus.Client(0)).KV
+
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("key"), Value: []byte("val")})
+ require.NoError(t, err)
+
+ _, err = kvc.Compact(t.Context(), &pb.CompactionRequest{Revision: 2})
+ require.NoError(t, err)
+
+ txn := &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestRange{
+ RequestRange: &pb.RangeRequest{
+ Key: []byte("key"),
+ Revision: 1,
+ },
+ },
+ },
+ {
+ Request: &pb.RequestOp_RequestPut{
+ RequestPut: &pb.PutRequest{
+ Key: []byte("should-not-exist"),
+ Value: []byte("inconsistent"),
+ },
+ },
+ },
+ },
+ }
+ _, err = kvc.Txn(t.Context(), txn)
+ require.ErrorContains(t, err, "mvcc: required revision has been compacted")
+
+ for i := range clus.Members {
+ resp, err := clus.Client(i).Get(t.Context(), "should-not-exist")
+ require.NoError(t, err)
+ require.Equalf(t, int64(0), resp.Count, "member %d should not have the key", i)
+ }
+}
diff --git a/tests/integration/utl_wal_version_test.go b/tests/integration/utl_wal_version_test.go
index 4eb4226151b8..26f8c87d0c4d 100644
--- a/tests/integration/utl_wal_version_test.go
+++ b/tests/integration/utl_wal_version_test.go
@@ -63,7 +63,7 @@ func TestEtcdVersionFromWAL(t *testing.T) {
// Once the cluster version has been updated, any entity's storage
// version should be align with cluster version.
- ctx, cancel := context.WithTimeout(context.Background(), testutil.RequestTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), testutil.RequestTimeout)
_, err = cli.AuthStatus(ctx)
cancel()
if err != nil {
@@ -74,7 +74,7 @@ func TestEtcdVersionFromWAL(t *testing.T) {
cli.Close()
srv.Close()
- w, err := wal.Open(zap.NewNop(), cfg.Dir+"/member/wal", walpb.Snapshot{})
+ w, err := wal.Open(zap.NewNop(), cfg.Dir+"/member/wal", &walpb.Snapshot{})
require.NoError(t, err)
defer w.Close()
diff --git a/tests/integration/v2store/store_tag_test.go b/tests/integration/v2store/store_tag_test.go
index b735104e8224..81196d9382b8 100644
--- a/tests/integration/v2store/store_tag_test.go
+++ b/tests/integration/v2store/store_tag_test.go
@@ -21,12 +21,12 @@ import (
"github.com/stretchr/testify/require"
"go.etcd.io/etcd/server/v3/etcdserver/api/v2store"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
)
// TestStoreRecover ensures that the store can recover from a previously saved state.
func TestStoreRecover(t *testing.T) {
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
s := v2store.New()
var eidx uint64 = 4
s.Create("/foo", true, "", false, v2store.TTLOptionSet{ExpireTime: v2store.Permanent})
diff --git a/tests/integration/v2store/store_test.go b/tests/integration/v2store/store_test.go
index 842b57deb62a..73f8fae83c8b 100644
--- a/tests/integration/v2store/store_test.go
+++ b/tests/integration/v2store/store_test.go
@@ -101,7 +101,7 @@ func TestSet(t *testing.T) {
assert.Equal(t, "set", e.Action)
assert.Equal(t, "/foo", e.Node.Key)
assert.False(t, e.Node.Dir)
- assert.Equal(t, "", *e.Node.Value)
+ assert.Empty(t, *e.Node.Value)
assert.Nil(t, e.Node.Nodes)
assert.Nil(t, e.Node.Expiration)
assert.Equal(t, int64(0), e.Node.TTL)
@@ -123,7 +123,7 @@ func TestSet(t *testing.T) {
// check prevNode
require.NotNil(t, e.PrevNode)
assert.Equal(t, "/foo", e.PrevNode.Key)
- assert.Equal(t, "", *e.PrevNode.Value)
+ assert.Empty(t, *e.PrevNode.Value)
assert.Equal(t, uint64(1), e.PrevNode.ModifiedIndex)
// Set /foo="baz" (for testing prevNode)
eidx = 3
@@ -198,7 +198,7 @@ func TestStoreCreateValue(t *testing.T) {
assert.Equal(t, "create", e.Action)
assert.Equal(t, "/empty", e.Node.Key)
assert.False(t, e.Node.Dir)
- assert.Equal(t, "", *e.Node.Value)
+ assert.Empty(t, *e.Node.Value)
assert.Nil(t, e.Node.Nodes)
assert.Nil(t, e.Node.Expiration)
assert.Equal(t, int64(0), e.Node.TTL)
@@ -271,7 +271,7 @@ func TestStoreUpdateValue(t *testing.T) {
assert.Equal(t, "update", e.Action)
assert.Equal(t, "/foo", e.Node.Key)
assert.False(t, e.Node.Dir)
- assert.Equal(t, "", *e.Node.Value)
+ assert.Empty(t, *e.Node.Value)
assert.Equal(t, int64(0), e.Node.TTL)
assert.Equal(t, uint64(3), e.Node.ModifiedIndex)
// check prevNode
@@ -282,7 +282,7 @@ func TestStoreUpdateValue(t *testing.T) {
e, _ = s.Get("/foo", false, false)
assert.Equal(t, eidx, e.EtcdIndex)
- assert.Equal(t, "", *e.Node.Value)
+ assert.Empty(t, *e.Node.Value)
}
// TestStoreUpdateFailsIfDirectory ensures that the store cannot update a directory.
diff --git a/tests/integration/v3_alarm_test.go b/tests/integration/v3_alarm_test.go
index 0f723b8f2c91..35ea115ec93b 100644
--- a/tests/integration/v3_alarm_test.go
+++ b/tests/integration/v3_alarm_test.go
@@ -56,23 +56,23 @@ func TestV3StorageQuotaApply(t *testing.T) {
// test small put still works
smallbuf := make([]byte, 1024)
- _, serr := kvc0.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf})
+ _, serr := kvc0.Put(t.Context(), &pb.PutRequest{Key: key, Value: smallbuf})
require.NoError(t, serr)
// test big put
bigbuf := make([]byte, quotasize)
- _, err := kvc1.Put(context.TODO(), &pb.PutRequest{Key: key, Value: bigbuf})
+ _, err := kvc1.Put(t.Context(), &pb.PutRequest{Key: key, Value: bigbuf})
require.NoError(t, err)
// quorum get should work regardless of whether alarm is raised
- _, err = kvc0.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ _, err = kvc0.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
// wait until alarm is raised for sure-- poll the alarms
stopc := time.After(5 * time.Second)
for {
req := &pb.AlarmRequest{Action: pb.AlarmRequest_GET}
- resp, aerr := clus.Members[0].Server.Alarm(context.TODO(), req)
+ resp, aerr := clus.Members[0].Server.Alarm(t.Context(), req)
require.NoError(t, aerr)
if len(resp.Alarms) != 0 {
break
@@ -85,7 +85,7 @@ func TestV3StorageQuotaApply(t *testing.T) {
}
// txn with non-mutating Ops should go through when NOSPACE alarm is raised
- _, err = kvc0.Txn(context.TODO(), &pb.TxnRequest{
+ _, err = kvc0.Txn(t.Context(), &pb.TxnRequest{
Compare: []*pb.Compare{
{
Key: key,
@@ -106,27 +106,24 @@ func TestV3StorageQuotaApply(t *testing.T) {
})
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.TODO(), integration.RequestWaitTimeout)
+ ctx, cancel := context.WithTimeout(t.Context(), integration.RequestWaitTimeout)
defer cancel()
// small quota machine should reject put
- if _, err := kvc0.Put(ctx, &pb.PutRequest{Key: key, Value: smallbuf}); err == nil {
- t.Fatalf("past-quota instance should reject put")
- }
+ _, err = kvc0.Put(ctx, &pb.PutRequest{Key: key, Value: smallbuf})
+ require.Errorf(t, err, "past-quota instance should reject put")
// large quota machine should reject put
- if _, err := kvc1.Put(ctx, &pb.PutRequest{Key: key, Value: smallbuf}); err == nil {
- t.Fatalf("past-quota instance should reject put")
- }
+ _, err = kvc1.Put(ctx, &pb.PutRequest{Key: key, Value: smallbuf})
+ require.Errorf(t, err, "past-quota instance should reject put")
// reset large quota node to ensure alarm persisted
clus.Members[1].Stop(t)
clus.Members[1].Restart(t)
clus.WaitMembersForLeader(t, clus.Members)
- if _, err := kvc1.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf}); err == nil {
- t.Fatalf("alarmed instance should reject put after reset")
- }
+ _, err = kvc1.Put(t.Context(), &pb.PutRequest{Key: key, Value: smallbuf})
+ require.Errorf(t, err, "alarmed instance should reject put after reset")
}
// TestV3AlarmDeactivate ensures that space alarms can be deactivated so puts go through.
@@ -143,21 +140,21 @@ func TestV3AlarmDeactivate(t *testing.T) {
Action: pb.AlarmRequest_ACTIVATE,
Alarm: pb.AlarmType_NOSPACE,
}
- _, err := mt.Alarm(context.TODO(), alarmReq)
+ _, err := mt.Alarm(t.Context(), alarmReq)
require.NoError(t, err)
key := []byte("abc")
smallbuf := make([]byte, 512)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: key, Value: smallbuf})
if err == nil && !eqErrGRPC(err, rpctypes.ErrGRPCNoSpace) {
t.Fatalf("put got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
}
alarmReq.Action = pb.AlarmRequest_DEACTIVATE
- _, err = mt.Alarm(context.TODO(), alarmReq)
+ _, err = mt.Alarm(t.Context(), alarmReq)
require.NoError(t, err)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: key, Value: smallbuf})
require.NoError(t, err)
}
@@ -172,7 +169,7 @@ func TestV3CorruptAlarm(t *testing.T) {
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
- if _, err := clus.Client(0).Put(context.TODO(), "k", "v"); err != nil {
+ if _, err := clus.Client(0).Put(t.Context(), "k", "v"); err != nil {
t.Error(err)
}
}()
@@ -197,11 +194,11 @@ func TestV3CorruptAlarm(t *testing.T) {
time.Sleep(time.Second * 2)
// Wait for cluster so Puts succeed in case member 0 was the leader.
- _, err := clus.Client(1).Get(context.TODO(), "k")
+ _, err := clus.Client(1).Get(t.Context(), "k")
require.NoError(t, err)
- _, err = clus.Client(1).Put(context.TODO(), "xyz", "321")
+ _, err = clus.Client(1).Put(t.Context(), "xyz", "321")
require.NoError(t, err)
- _, err = clus.Client(1).Put(context.TODO(), "abc", "fed")
+ _, err = clus.Client(1).Put(t.Context(), "abc", "fed")
require.NoError(t, err)
// Restart with corruption checking enabled.
@@ -215,18 +212,16 @@ func TestV3CorruptAlarm(t *testing.T) {
time.Sleep(time.Second * 2)
clus.Members[0].WaitStarted(t)
- resp0, err0 := clus.Client(0).Get(context.TODO(), "abc")
+ resp0, err0 := clus.Client(0).Get(t.Context(), "abc")
require.NoError(t, err0)
clus.Members[1].WaitStarted(t)
- resp1, err1 := clus.Client(1).Get(context.TODO(), "abc")
+ resp1, err1 := clus.Client(1).Get(t.Context(), "abc")
require.NoError(t, err1)
- if resp0.Kvs[0].ModRevision == resp1.Kvs[0].ModRevision {
- t.Fatalf("matching ModRevision values")
- }
+ require.NotEqualf(t, resp0.Kvs[0].ModRevision, resp1.Kvs[0].ModRevision, "matching ModRevision values")
for i := 0; i < 5; i++ {
- presp, perr := clus.Client(0).Put(context.TODO(), "abc", "aaa")
+ presp, perr := clus.Client(0).Put(t.Context(), "abc", "aaa")
if perr != nil {
if eqErrGRPC(perr, rpctypes.ErrCorrupt) {
return
@@ -250,7 +245,7 @@ func TestV3CorruptAlarmWithLeaseCorrupted(t *testing.T) {
})
defer clus.Terminate(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(ctx, &pb.LeaseGrantRequest{ID: 1, TTL: 60})
@@ -302,18 +297,16 @@ func TestV3CorruptAlarmWithLeaseCorrupted(t *testing.T) {
// Revoke lease should remove key except the member with corruption
_, err = integration.ToGRPC(clus.Members[0].Client).Lease.LeaseRevoke(ctx, &pb.LeaseRevokeRequest{ID: lresp.ID})
require.NoError(t, err)
- resp0, err0 := clus.Members[1].Client.KV.Get(context.TODO(), "foo")
+ resp0, err0 := clus.Members[1].Client.KV.Get(t.Context(), "foo")
require.NoError(t, err0)
- resp1, err1 := clus.Members[2].Client.KV.Get(context.TODO(), "foo")
+ resp1, err1 := clus.Members[2].Client.KV.Get(t.Context(), "foo")
require.NoError(t, err1)
- if resp0.Header.Revision == resp1.Header.Revision {
- t.Fatalf("matching Revision values")
- }
+ require.NotEqualf(t, resp0.Header.Revision, resp1.Header.Revision, "matching Revision values")
// Wait for CorruptCheckTime
time.Sleep(time.Second)
- presp, perr := clus.Client(0).Put(context.TODO(), "abc", "aaa")
+ presp, perr := clus.Client(0).Put(t.Context(), "abc", "aaa")
if perr != nil {
if eqErrGRPC(perr, rpctypes.ErrCorrupt) {
return
diff --git a/tests/integration/v3_auth_test.go b/tests/integration/v3_auth_test.go
index 84175720e92a..6703f7fd926e 100644
--- a/tests/integration/v3_auth_test.go
+++ b/tests/integration/v3_auth_test.go
@@ -17,6 +17,7 @@ package integration
import (
"context"
"fmt"
+ "strings"
"sync"
"testing"
"time"
@@ -37,16 +38,14 @@ func TestV3AuthEmptyUserGet(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
api := integration.ToGRPC(clus.Client(0))
authSetupRoot(t, api.Auth)
_, err := api.KV.Range(ctx, &pb.RangeRequest{Key: []byte("abc")})
- if !eqErrGRPC(err, rpctypes.ErrUserEmpty) {
- t.Fatalf("got %v, expected %v", err, rpctypes.ErrUserEmpty)
- }
+ require.Truef(t, eqErrGRPC(err, rpctypes.ErrUserEmpty), "got %v, expected %v", err, rpctypes.ErrUserEmpty)
}
// TestV3AuthEmptyUserPut ensures that a put with an empty user will return an empty user error,
@@ -59,7 +58,7 @@ func TestV3AuthEmptyUserPut(t *testing.T) {
})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
api := integration.ToGRPC(clus.Client(0))
@@ -70,9 +69,7 @@ func TestV3AuthEmptyUserPut(t *testing.T) {
// cluster terminating.
for i := 0; i < 10; i++ {
_, err := api.KV.Put(ctx, &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
- if !eqErrGRPC(err, rpctypes.ErrUserEmpty) {
- t.Fatalf("got %v, expected %v", err, rpctypes.ErrUserEmpty)
- }
+ require.Truef(t, eqErrGRPC(err, rpctypes.ErrUserEmpty), "got %v, expected %v", err, rpctypes.ErrUserEmpty)
}
}
@@ -89,7 +86,7 @@ func TestV3AuthTokenWithDisable(t *testing.T) {
require.NoError(t, cerr)
defer c.Close()
- rctx, cancel := context.WithCancel(context.TODO())
+ rctx, cancel := context.WithCancel(t.Context())
donec := make(chan struct{})
go func() {
defer close(donec)
@@ -99,7 +96,7 @@ func TestV3AuthTokenWithDisable(t *testing.T) {
}()
time.Sleep(10 * time.Millisecond)
- _, err := c.AuthDisable(context.TODO())
+ _, err := c.AuthDisable(t.Context())
require.NoError(t, err)
time.Sleep(10 * time.Millisecond)
@@ -114,19 +111,17 @@ func TestV3AuthRevision(t *testing.T) {
api := integration.ToGRPC(clus.Client(0))
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
presp, perr := api.KV.Put(ctx, &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
cancel()
require.NoError(t, perr)
rev := presp.Header.Revision
- ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
+ ctx, cancel = context.WithTimeout(t.Context(), 5*time.Second)
aresp, aerr := api.Auth.UserAdd(ctx, &pb.AuthUserAddRequest{Name: "root", Password: "123", Options: &authpb.UserAddOptions{NoPassword: false}})
cancel()
require.NoError(t, aerr)
- if aresp.Header.Revision != rev {
- t.Fatalf("revision expected %d, got %d", rev, aresp.Header.Revision)
- }
+ require.Equalf(t, aresp.Header.Revision, rev, "revision expected %d, got %d", rev, aresp.Header.Revision)
}
// TestV3AuthWithLeaseRevokeWithRoot ensures that granted leases
@@ -158,18 +153,18 @@ func testV3AuthWithLeaseRevokeWithRoot(t *testing.T, ccfg integration.ClusterCon
require.NoError(t, cerr)
defer rootc.Close()
- leaseResp, err := rootc.Grant(context.TODO(), 2)
+ leaseResp, err := rootc.Grant(t.Context(), 2)
require.NoError(t, err)
leaseID := leaseResp.ID
- _, err = rootc.Put(context.TODO(), "foo", "bar", clientv3.WithLease(leaseID))
+ _, err = rootc.Put(t.Context(), "foo", "bar", clientv3.WithLease(leaseID))
require.NoError(t, err)
// wait for lease expire
time.Sleep(3 * time.Second)
tresp, terr := rootc.TimeToLive(
- context.TODO(),
+ t.Context(),
leaseID,
clientv3.WithAttachedKeys(),
)
@@ -188,6 +183,7 @@ type user struct {
name string
password string
role string
+ perm string
key string
end string
}
@@ -214,102 +210,22 @@ func TestV3AuthWithLeaseRevoke(t *testing.T) {
require.NoError(t, cerr)
defer rootc.Close()
- leaseResp, err := rootc.Grant(context.TODO(), 90)
+ leaseResp, err := rootc.Grant(t.Context(), 90)
require.NoError(t, err)
leaseID := leaseResp.ID
// permission of k3 isn't granted to user1
- _, err = rootc.Put(context.TODO(), "k3", "val", clientv3.WithLease(leaseID))
+ _, err = rootc.Put(t.Context(), "k3", "val", clientv3.WithLease(leaseID))
require.NoError(t, err)
userc, cerr := integration.NewClient(t, clientv3.Config{Endpoints: clus.Client(0).Endpoints(), Username: "user1", Password: "user1-123"})
require.NoError(t, cerr)
defer userc.Close()
- _, err = userc.Revoke(context.TODO(), leaseID)
+ _, err = userc.Revoke(t.Context(), leaseID)
if err == nil {
t.Fatal("revoking from user1 should be failed with permission denied")
}
}
-func TestV3AuthWithLeaseRenew(t *testing.T) {
- integration.BeforeTest(t)
- clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
- defer clus.Terminate(t)
-
- users := []user{
- {
- name: "test-user",
- password: "test-user-123",
- role: "test-role",
- // test-user can only write keys in [k1, k3), i.e. k1 and k2.
- key: "k1",
- end: "k3",
- },
- }
- authSetupUsers(t, integration.ToGRPC(clus.Client(0)).Auth, users)
- authSetupRoot(t, integration.ToGRPC(clus.Client(0)).Auth)
-
- rootCli, cerr := integration.NewClient(t, clientv3.Config{
- Endpoints: clus.Client(0).Endpoints(),
- Username: "root",
- Password: "123",
- })
- require.NoError(t, cerr)
- defer rootCli.Close()
-
- testUserClis := []*clientv3.Client{}
- for i := 0; i < len(clus.Members); i++ {
- testUserCli, err := integration.NewClient(t, clientv3.Config{
- Endpoints: clus.Client(i).Endpoints(),
- Username: "test-user",
- Password: "test-user-123",
- })
- require.NoError(t, err)
- defer testUserCli.Close()
-
- testUserClis = append(testUserClis, testUserCli)
- }
-
- anonCli, cerr := integration.NewClient(t, clientv3.Config{
- Endpoints: clus.Client(0).Endpoints(),
- })
- require.NoError(t, cerr)
- defer anonCli.Close()
-
- leaseResp, err := rootCli.Grant(t.Context(), 90)
- require.NoError(t, err)
- leaseID := leaseResp.ID
-
- _, err = rootCli.Put(t.Context(), "k1", "val", clientv3.WithLease(leaseID))
- require.NoError(t, err)
- _, err = rootCli.Put(t.Context(), "k3", "val", clientv3.WithLease(leaseID))
- require.NoError(t, err)
-
- _, err = anonCli.KeepAliveOnce(t.Context(), leaseID)
- require.ErrorContainsf(t, err, "etcdserver: user name is empty", "should reject renew")
-
- _, err = rootCli.KeepAliveOnce(t.Context(), leaseID)
- require.NoError(t, err)
-
- for _, testUserCli := range testUserClis {
- _, err = testUserCli.KeepAliveOnce(t.Context(), leaseID)
- require.ErrorContainsf(t, err, "etcdserver: permission denied", "[%v] should reject renew", testUserCli.Endpoints())
- }
-
- leaseResp, err = rootCli.Grant(t.Context(), 90)
- require.NoError(t, err)
- leaseID = leaseResp.ID
-
- _, err = rootCli.Put(t.Context(), "k1", "val", clientv3.WithLease(leaseID))
- require.NoError(t, err)
- _, err = rootCli.Put(t.Context(), "k2", "val", clientv3.WithLease(leaseID))
- require.NoError(t, err)
-
- for _, testUserCli := range testUserClis {
- _, err = testUserCli.KeepAliveOnce(t.Context(), leaseID)
- require.NoErrorf(t, err, "[%v] should accept renew", testUserCli.Endpoints())
- }
-}
-
func TestV3AuthWithLeaseAttach(t *testing.T) {
integration.BeforeTest(t)
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
@@ -343,24 +259,24 @@ func TestV3AuthWithLeaseAttach(t *testing.T) {
require.NoError(t, cerr)
defer user2c.Close()
- leaseResp, err := user1c.Grant(context.TODO(), 90)
+ leaseResp, err := user1c.Grant(t.Context(), 90)
require.NoError(t, err)
leaseID := leaseResp.ID
// permission of k2 is also granted to user2
- _, err = user1c.Put(context.TODO(), "k2", "val", clientv3.WithLease(leaseID))
+ _, err = user1c.Put(t.Context(), "k2", "val", clientv3.WithLease(leaseID))
require.NoError(t, err)
- _, err = user2c.Revoke(context.TODO(), leaseID)
+ _, err = user2c.Revoke(t.Context(), leaseID)
require.NoError(t, err)
- leaseResp, err = user1c.Grant(context.TODO(), 90)
+ leaseResp, err = user1c.Grant(t.Context(), 90)
require.NoError(t, err)
leaseID = leaseResp.ID
// permission of k1 isn't granted to user2
- _, err = user1c.Put(context.TODO(), "k1", "val", clientv3.WithLease(leaseID))
+ _, err = user1c.Put(t.Context(), "k1", "val", clientv3.WithLease(leaseID))
require.NoError(t, err)
- _, err = user2c.Revoke(context.TODO(), leaseID)
+ _, err = user2c.Revoke(t.Context(), leaseID)
if err == nil {
t.Fatal("revoking from user2 should be failed with permission denied")
}
@@ -368,23 +284,30 @@ func TestV3AuthWithLeaseAttach(t *testing.T) {
func authSetupUsers(t *testing.T, auth pb.AuthClient, users []user) {
for _, user := range users {
- _, err := auth.UserAdd(context.TODO(), &pb.AuthUserAddRequest{Name: user.name, Password: user.password, Options: &authpb.UserAddOptions{NoPassword: false}})
+ _, err := auth.UserAdd(t.Context(), &pb.AuthUserAddRequest{Name: user.name, Password: user.password, Options: &authpb.UserAddOptions{NoPassword: false}})
require.NoError(t, err)
- _, err = auth.RoleAdd(context.TODO(), &pb.AuthRoleAddRequest{Name: user.role})
+ _, err = auth.RoleAdd(t.Context(), &pb.AuthRoleAddRequest{Name: user.role})
require.NoError(t, err)
- _, err = auth.UserGrantRole(context.TODO(), &pb.AuthUserGrantRoleRequest{User: user.name, Role: user.role})
+ _, err = auth.UserGrantRole(t.Context(), &pb.AuthUserGrantRoleRequest{User: user.name, Role: user.role})
require.NoError(t, err)
if len(user.key) == 0 {
continue
}
+ permType := authpb.Permission_READWRITE
+ if len(user.perm) > 0 {
+ val, ok := authpb.Permission_Type_value[strings.ToUpper(user.perm)]
+ if ok {
+ permType = authpb.Permission_Type(val)
+ }
+ }
perm := &authpb.Permission{
- PermType: authpb.READWRITE,
+ PermType: permType,
Key: []byte(user.key),
RangeEnd: []byte(user.end),
}
- _, err = auth.RoleGrantPermission(context.TODO(), &pb.AuthRoleGrantPermissionRequest{Name: user.role, Perm: perm})
+ _, err = auth.RoleGrantPermission(t.Context(), &pb.AuthRoleGrantPermissionRequest{Name: user.role, Perm: perm})
require.NoError(t, err)
}
}
@@ -399,7 +322,7 @@ func authSetupRoot(t *testing.T, auth pb.AuthClient) {
},
}
authSetupUsers(t, auth, root)
- _, err := auth.AuthEnable(context.TODO(), &pb.AuthEnableRequest{})
+ _, err := auth.AuthEnable(t.Context(), &pb.AuthEnableRequest{})
require.NoError(t, err)
}
@@ -412,17 +335,13 @@ func TestV3AuthNonAuthorizedRPCs(t *testing.T) {
key := "foo"
val := "bar"
- _, err := nonAuthedKV.Put(context.TODO(), key, val)
- if err != nil {
- t.Fatalf("couldn't put key (%v)", err)
- }
+ _, err := nonAuthedKV.Put(t.Context(), key, val)
+ require.NoErrorf(t, err, "couldn't put key (%v)", err)
authSetupRoot(t, integration.ToGRPC(clus.Client(0)).Auth)
- respput, err := nonAuthedKV.Put(context.TODO(), key, val)
- if !eqErrGRPC(err, rpctypes.ErrGRPCUserEmpty) {
- t.Fatalf("could put key (%v), it should cause an error of permission denied", respput)
- }
+ respput, err := nonAuthedKV.Put(t.Context(), key, val)
+ require.Truef(t, eqErrGRPC(err, rpctypes.ErrGRPCUserEmpty), "could put key (%v), it should cause an error of permission denied", respput)
}
func TestV3AuthNestedTxnPermissionDenied(t *testing.T) {
@@ -439,9 +358,8 @@ func TestV3AuthNestedTxnPermissionDenied(t *testing.T) {
end: "zoo",
},
}
- anonCli := integration.ToGRPC(clus.Client(0))
- authSetupUsers(t, anonCli.Auth, users)
- authSetupRoot(t, anonCli.Auth)
+ authSetupUsers(t, integration.ToGRPC(clus.Client(0)).Auth, users)
+ authSetupRoot(t, integration.ToGRPC(clus.Client(0)).Auth)
rootc, err := integration.NewClient(t, clientv3.Config{
Endpoints: clus.Client(0).Endpoints(),
@@ -462,21 +380,27 @@ func TestV3AuthNestedTxnPermissionDenied(t *testing.T) {
_, err = rootc.Put(t.Context(), "boo", "bar")
require.NoError(t, err)
- _, err = userc.Txn(t.Context()).
- Then(clientv3.OpTxn(
- nil,
- []clientv3.Op{
- clientv3.OpTxn(
- nil,
- []clientv3.Op{
- clientv3.OpDelete("boo"),
+ txn := &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestTxn{
+ RequestTxn: &pb.TxnRequest{
+ Success: []*pb.RequestOp{
+ {
+ Request: &pb.RequestOp_RequestDeleteRange{
+ RequestDeleteRange: &pb.DeleteRangeRequest{
+ Key: []byte("boo"),
+ },
+ },
+ },
+ },
},
- nil,
- ),
+ },
},
- nil,
- )).Commit()
+ },
+ }
+ _, err = integration.ToGRPC(userc).KV.Txn(t.Context(), txn)
require.Error(t, err)
require.Truef(t, eqErrGRPC(err, rpctypes.ErrGRPCPermissionDenied), "got %v, expected %v", err, rpctypes.ErrGRPCPermissionDenied)
@@ -486,6 +410,102 @@ func TestV3AuthNestedTxnPermissionDenied(t *testing.T) {
require.Equal(t, resp.Kvs[0].Value, []byte("bar"))
}
+func TestReadWithPrevKvInTXN(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ users := []user{
+ {
+ name: "user1",
+ password: "user1-123",
+ role: "role1",
+ perm: "write",
+ key: "foo",
+ end: "zoo",
+ },
+ }
+ anonCli := integration.ToGRPC(clus.Client(0))
+ authSetupUsers(t, anonCli.Auth, users)
+ authSetupRoot(t, anonCli.Auth)
+
+ rootc, err := integration.NewClient(t, clientv3.Config{
+ Endpoints: clus.Client(0).Endpoints(),
+ Username: "root",
+ Password: "123",
+ })
+ require.NoError(t, err)
+ defer rootc.Close()
+
+ userc, err := integration.NewClient(t, clientv3.Config{
+ Endpoints: clus.Client(0).Endpoints(),
+ Username: "user1",
+ Password: "user1-123",
+ })
+ require.NoError(t, err)
+ defer userc.Close()
+
+ _, err = rootc.Put(t.Context(), "foo", "bar")
+ require.NoError(t, err)
+
+ _, err = userc.Txn(t.Context()).
+ Then(clientv3.OpPut("foo", "new", clientv3.WithPrevKV())).
+ Commit()
+
+ require.Error(t, err)
+ require.Truef(t, eqErrGRPC(err, rpctypes.ErrGRPCPermissionDenied), "got %v, expected %v", err, rpctypes.ErrGRPCPermissionDenied)
+}
+
+func TestPutWithLeaseInTXN(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ users := []user{
+ {
+ name: "user1",
+ password: "user1-123",
+ role: "role1",
+ perm: "write",
+ key: "foo",
+ end: "fop",
+ },
+ }
+ anonCli := integration.ToGRPC(clus.Client(0))
+ authSetupUsers(t, anonCli.Auth, users)
+ authSetupRoot(t, anonCli.Auth)
+
+ rootc, err := integration.NewClient(t, clientv3.Config{
+ Endpoints: clus.Client(0).Endpoints(),
+ Username: "root",
+ Password: "123",
+ })
+ require.NoError(t, err)
+ defer rootc.Close()
+
+ userc, err := integration.NewClient(t, clientv3.Config{
+ Endpoints: clus.Client(0).Endpoints(),
+ Username: "user1",
+ Password: "user1-123",
+ })
+ require.NoError(t, err)
+ defer userc.Close()
+
+ t.Log("Create a lease and attach it to a key which the user1 doesn't have permission to write")
+ leaseResp, err := rootc.Grant(t.Context(), 90)
+ require.NoError(t, err)
+ leaseID := leaseResp.ID
+ _, err = rootc.Put(t.Context(), "eoo", "bar", clientv3.WithLease(leaseID))
+ require.NoError(t, err)
+
+ _, err = userc.Txn(t.Context()).
+ Then(clientv3.OpPut("foo", "new", clientv3.WithLease(leaseID))).
+ Commit()
+
+ require.Error(t, err)
+ require.Truef(t, eqErrGRPC(err, rpctypes.ErrGRPCPermissionDenied), "got %v, expected %v", err, rpctypes.ErrGRPCPermissionDenied)
+}
+
func TestV3AuthOldRevConcurrent(t *testing.T) {
integration.BeforeTest(t)
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
@@ -506,13 +526,13 @@ func TestV3AuthOldRevConcurrent(t *testing.T) {
f := func(i int) {
defer wg.Done()
role, user := fmt.Sprintf("test-role-%d", i), fmt.Sprintf("test-user-%d", i)
- _, err := c.RoleAdd(context.TODO(), role)
+ _, err := c.RoleAdd(t.Context(), role)
require.NoError(t, err)
- _, err = c.RoleGrantPermission(context.TODO(), role, "\x00", clientv3.GetPrefixRangeEnd(""), clientv3.PermissionType(clientv3.PermReadWrite))
+ _, err = c.RoleGrantPermission(t.Context(), role, "\x00", clientv3.GetPrefixRangeEnd(""), clientv3.PermissionType(clientv3.PermReadWrite))
require.NoError(t, err)
- _, err = c.UserAdd(context.TODO(), user, "123")
+ _, err = c.UserAdd(t.Context(), user, "123")
require.NoError(t, err)
- _, err = c.Put(context.TODO(), "a", "b")
+ _, err = c.Put(t.Context(), "a", "b")
assert.NoError(t, err)
}
// needs concurrency to trigger
@@ -529,7 +549,7 @@ func TestV3AuthWatchErrorAndWatchId0(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
users := []user{
@@ -569,9 +589,7 @@ func TestV3AuthWatchErrorAndWatchId0(t *testing.T) {
require.Error(t, watchResponse.Err()) // permission denied
_, err := c.Put(ctx, "k1", "val")
- if err != nil {
- t.Fatalf("Unexpected error from Put: %v", err)
- }
+ require.NoErrorf(t, err, "Unexpected error from Put: %v", err)
<-watchEndCh
}
diff --git a/tests/integration/v3_election_test.go b/tests/integration/v3_election_test.go
index 94665dca613a..d84d8629363a 100644
--- a/tests/integration/v3_election_test.go
+++ b/tests/integration/v3_election_test.go
@@ -57,7 +57,7 @@ func TestElectionWait(t *testing.T) {
}
b := concurrency.NewElection(session, "test-election")
- cctx, cancel := context.WithCancel(context.TODO())
+ cctx, cancel := context.WithCancel(t.Context())
defer cancel()
s, ok := <-b.Observe(cctx)
if !ok {
@@ -83,7 +83,7 @@ func TestElectionWait(t *testing.T) {
e := concurrency.NewElection(session, "test-election")
ev := fmt.Sprintf("electval-%v", time.Now().UnixNano())
- if err := e.Campaign(context.TODO(), ev); err != nil {
+ if err := e.Campaign(t.Context(), ev); err != nil {
t.Errorf("failed volunteer (%v)", err)
}
// wait for followers to accept leadership
@@ -94,7 +94,7 @@ func TestElectionWait(t *testing.T) {
}
}
// let next leader take over
- if err := e.Resign(context.TODO()); err != nil {
+ if err := e.Resign(t.Context()); err != nil {
t.Errorf("failed resign (%v)", err)
}
// tell followers to start listening for next leader
@@ -116,7 +116,7 @@ func TestElectionFailover(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- cctx, cancel := context.WithCancel(context.TODO())
+ cctx, cancel := context.WithCancel(t.Context())
defer cancel()
ss := make([]*concurrency.Session, 3)
@@ -132,42 +132,33 @@ func TestElectionFailover(t *testing.T) {
// first leader (elected)
e := concurrency.NewElection(ss[0], "test-election")
- if err := e.Campaign(context.TODO(), "foo"); err != nil {
- t.Fatalf("failed volunteer (%v)", err)
- }
+ err := e.Campaign(t.Context(), "foo")
+ require.NoErrorf(t, err, "failed volunteer")
// check first leader
resp, ok := <-e.Observe(cctx)
- if !ok {
- t.Fatalf("could not wait for first election; channel closed")
- }
+ require.Truef(t, ok, "could not wait for first election; channel closed")
s := string(resp.Kvs[0].Value)
- if s != "foo" {
- t.Fatalf("wrong election result. got %s, wanted foo", s)
- }
+ require.Equalf(t, "foo", s, "wrong election result. got %s, wanted foo", s)
// next leader
electedErrC := make(chan error, 1)
go func() {
ee := concurrency.NewElection(ss[1], "test-election")
- eer := ee.Campaign(context.TODO(), "bar")
+ eer := ee.Campaign(t.Context(), "bar")
electedErrC <- eer // If eer != nil, the test will fail by calling t.Fatal(eer)
}()
// invoke leader failover
- err := ss[0].Close()
+ err = ss[0].Close()
require.NoError(t, err)
// check new leader
e = concurrency.NewElection(ss[2], "test-election")
resp, ok = <-e.Observe(cctx)
- if !ok {
- t.Fatalf("could not wait for second election; channel closed")
- }
+ require.Truef(t, ok, "could not wait for second election; channel closed")
s = string(resp.Kvs[0].Value)
- if s != "bar" {
- t.Fatalf("wrong election result. got %s, wanted bar", s)
- }
+ require.Equalf(t, "bar", s, "wrong election result. got %s, wanted bar", s)
// leader must ack election (otherwise, Campaign may see closed conn)
eer := <-electedErrC
@@ -189,13 +180,13 @@ func TestElectionSessionRecampaign(t *testing.T) {
defer session.Orphan()
e := concurrency.NewElection(session, "test-elect")
- err = e.Campaign(context.TODO(), "abc")
+ err = e.Campaign(t.Context(), "abc")
require.NoError(t, err)
e2 := concurrency.NewElection(session, "test-elect")
- err = e2.Campaign(context.TODO(), "def")
+ err = e2.Campaign(t.Context(), "def")
require.NoError(t, err)
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
if resp := <-e.Observe(ctx); len(resp.Kvs) == 0 || string(resp.Kvs[0].Value) != "def" {
t.Fatalf("expected value=%q, got response %v", "def", resp)
@@ -212,12 +203,12 @@ func TestElectionOnPrefixOfExistingKey(t *testing.T) {
defer clus.Terminate(t)
cli := clus.RandClient()
- _, err := cli.Put(context.TODO(), "testa", "value")
+ _, err := cli.Put(t.Context(), "testa", "value")
require.NoError(t, err)
s, serr := concurrency.NewSession(cli)
require.NoError(t, serr)
e := concurrency.NewElection(s, "test")
- ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
err = e.Campaign(ctx, "abc")
cancel()
// after 5 seconds, deadlock results in
@@ -238,13 +229,13 @@ func TestElectionOnSessionRestart(t *testing.T) {
require.NoError(t, err)
e := concurrency.NewElection(session, "test-elect")
- require.NoError(t, e.Campaign(context.TODO(), "abc"))
+ require.NoError(t, e.Campaign(t.Context(), "abc"))
// ensure leader is not lost to waiter on fail-over
waitSession, werr := concurrency.NewSession(cli)
require.NoError(t, werr)
defer waitSession.Orphan()
- waitCtx, waitCancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ waitCtx, waitCancel := context.WithTimeout(t.Context(), 5*time.Second)
defer waitCancel()
go concurrency.NewElection(waitSession, "test-elect").Campaign(waitCtx, "123")
@@ -254,9 +245,9 @@ func TestElectionOnSessionRestart(t *testing.T) {
defer newSession.Orphan()
newElection := concurrency.NewElection(newSession, "test-elect")
- require.NoError(t, newElection.Campaign(context.TODO(), "def"))
+ require.NoError(t, newElection.Campaign(t.Context(), "def"))
- ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
defer cancel()
if resp := <-newElection.Observe(ctx); len(resp.Kvs) == 0 || string(resp.Kvs[0].Value) != "def" {
t.Errorf("expected value=%q, got response %v", "def", resp)
@@ -277,20 +268,72 @@ func TestElectionObserveCompacted(t *testing.T) {
defer session.Orphan()
e := concurrency.NewElection(session, "test-elect")
- require.NoError(t, e.Campaign(context.TODO(), "abc"))
+ require.NoError(t, e.Campaign(t.Context(), "abc"))
- presp, perr := cli.Put(context.TODO(), "foo", "bar")
+ presp, perr := cli.Put(t.Context(), "foo", "bar")
require.NoError(t, perr)
- _, cerr := cli.Compact(context.TODO(), presp.Header.Revision)
+ _, cerr := cli.Compact(t.Context(), presp.Header.Revision)
require.NoError(t, cerr)
- v, ok := <-e.Observe(context.TODO())
+ v, ok := <-e.Observe(t.Context())
if !ok {
t.Fatal("failed to observe on compacted revision")
}
- if string(v.Kvs[0].Value) != "abc" {
- t.Fatalf(`expected leader value "abc", got %q`, string(v.Kvs[0].Value))
+ require.Equalf(t, "abc", string(v.Kvs[0].Value), `expected leader value "abc", got %q`, string(v.Kvs[0].Value))
+}
+
+func TestElectionObserveFreshResponsesOnProclaim(t *testing.T) {
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ cli := clus.RandClient()
+
+ session, err := concurrency.NewSession(cli)
+ require.NoError(t, err)
+ defer session.Orphan()
+
+ e := concurrency.NewElection(session, "test-elect")
+ require.NoError(t, e.Campaign(t.Context(), "abc"))
+
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
+ defer cancel()
+
+ observeCh := e.Observe(ctx)
+
+ mustObserve := func(want string, msg string) *clientv3.GetResponse {
+ resp, ok := <-observeCh
+ require.Truef(t, ok, msg)
+ require.Len(t, resp.Kvs, 1)
+ require.Equal(t, want, string(resp.Kvs[0].Value))
+ return resp
+ }
+
+ assertFreshResponse := func(lhs, rhs *clientv3.GetResponse, msg string) {
+ if lhs == rhs {
+ t.Fatal(msg)
+ }
}
+
+ firstResp := mustObserve("abc", "could not wait for first election; channel closed")
+
+ require.NoError(t, e.Proclaim(t.Context(), "def"))
+ secondResp := mustObserve("def", "could not wait for proclaimed election; channel closed")
+
+ require.NoError(t, e.Proclaim(t.Context(), "ghi"))
+ thirdResp := mustObserve("ghi", "could not wait for second proclaimed election; channel closed")
+
+ assertFreshResponse(firstResp, secondResp, "Observe() reused the first GetResponse pointer")
+ assertFreshResponse(firstResp, thirdResp, "Observe() reused the first GetResponse pointer")
+ assertFreshResponse(secondResp, thirdResp, "Observe() reused the second GetResponse pointer")
+
+ require.Equal(t, "abc", string(firstResp.Kvs[0].Value))
+ require.Equal(t, "def", string(secondResp.Kvs[0].Value))
+ require.Equal(t, int64(1), firstResp.Kvs[0].Version)
+ require.Equal(t, int64(2), secondResp.Kvs[0].Version)
+ require.Equal(t, int64(3), thirdResp.Kvs[0].Version)
+ require.Less(t, firstResp.Header.Revision, secondResp.Header.Revision)
+ require.Less(t, secondResp.Header.Revision, thirdResp.Header.Revision)
}
// TestElectionWithAuthEnabled verifies the election interface when auth is enabled.
@@ -379,7 +422,7 @@ func TestElectionWithAuthEnabled(t *testing.T) {
s.Orphan()
e := concurrency.NewElection(s, campaign.pfx)
- eerr := e.Campaign(context.Background(), "whatever")
+ eerr := e.Campaign(t.Context(), "whatever")
if eerr != nil {
errC <- fmt.Errorf("[Campaign] %s: %w", campaign.name, eerr)
}
diff --git a/tests/integration/v3_failover_test.go b/tests/integration/v3_failover_test.go
index f9d71ffdaeef..8375d31dbf0e 100644
--- a/tests/integration/v3_failover_test.go
+++ b/tests/integration/v3_failover_test.go
@@ -23,18 +23,17 @@ import (
"time"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3"
- integration2 "go.etcd.io/etcd/tests/v3/framework/integration"
+ "go.etcd.io/etcd/tests/v3/framework/integration"
clientv3test "go.etcd.io/etcd/tests/v3/integration/clientv3"
)
func TestFailover(t *testing.T) {
cases := []struct {
name string
- testFunc func(*testing.T, *tls.Config, *integration2.Cluster) (*clientv3.Client, error)
+ testFunc func(*testing.T, *tls.Config, *integration.Cluster) (*clientv3.Client, error)
}{
{
name: "create client before the first server down",
@@ -49,21 +48,19 @@ func TestFailover(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Logf("Starting test [%s]", tc.name)
- integration2.BeforeTest(t)
+ integration.BeforeTest(t)
// Launch an etcd cluster with 3 members
t.Logf("Launching an etcd cluster with 3 members [%s]", tc.name)
- clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 3, ClientTLS: &integration2.TestTLSInfo})
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3, ClientTLS: &integration.TestTLSInfo})
defer clus.Terminate(t)
- cc, err := integration2.TestTLSInfo.ClientConfig()
+ cc, err := integration.TestTLSInfo.ClientConfig()
require.NoError(t, err)
// Create an etcd client before or after first server down
t.Logf("Creating an etcd client [%s]", tc.name)
cli, err := tc.testFunc(t, cc, clus)
- if err != nil {
- t.Fatalf("Failed to create client: %v", err)
- }
+ require.NoErrorf(t, err, "Failed to create client")
defer cli.Close()
// Sanity test
@@ -77,7 +74,7 @@ func TestFailover(t *testing.T) {
}
}
-func createClientBeforeServerDown(t *testing.T, cc *tls.Config, clus *integration2.Cluster) (*clientv3.Client, error) {
+func createClientBeforeServerDown(t *testing.T, cc *tls.Config, clus *integration.Cluster) (*clientv3.Client, error) {
cli, err := createClient(t, cc, clus)
if err != nil {
return nil, err
@@ -86,16 +83,15 @@ func createClientBeforeServerDown(t *testing.T, cc *tls.Config, clus *integratio
return cli, nil
}
-func createClientAfterServerDown(t *testing.T, cc *tls.Config, clus *integration2.Cluster) (*clientv3.Client, error) {
+func createClientAfterServerDown(t *testing.T, cc *tls.Config, clus *integration.Cluster) (*clientv3.Client, error) {
clus.Members[0].Close()
return createClient(t, cc, clus)
}
-func createClient(t *testing.T, cc *tls.Config, clus *integration2.Cluster) (*clientv3.Client, error) {
- cli, err := integration2.NewClient(t, clientv3.Config{
+func createClient(t *testing.T, cc *tls.Config, clus *integration.Cluster) (*clientv3.Client, error) {
+ cli, err := integration.NewClient(t, clientv3.Config{
Endpoints: clus.Endpoints(),
DialTimeout: 5 * time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
TLS: cc,
})
if err != nil {
@@ -110,7 +106,7 @@ func putWithRetries(t *testing.T, cli *clientv3.Client, key, val string, retryCo
// put data test
err := func() error {
t.Log("Sanity test, putting data")
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
defer cancel()
if _, putErr := cli.Put(ctx, key, val); putErr != nil {
@@ -135,19 +131,15 @@ func getWithRetries(t *testing.T, cli *clientv3.Client, key, val string, retryCo
// get data test
err := func() error {
t.Log("Sanity test, getting data")
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
defer cancel()
resp, getErr := cli.Get(ctx, key)
if getErr != nil {
t.Logf("Failed to get key (%v)", getErr)
return getErr
}
- if len(resp.Kvs) != 1 {
- t.Fatalf("Expected 1 key, got %d", len(resp.Kvs))
- }
- if !bytes.Equal([]byte(val), resp.Kvs[0].Value) {
- t.Fatalf("Unexpected value, expected: %s, got: %s", val, resp.Kvs[0].Value)
- }
+ require.Lenf(t, resp.Kvs, 1, "Expected 1 key, got %d", len(resp.Kvs))
+ require.Truef(t, bytes.Equal([]byte(val), resp.Kvs[0].Value), "Unexpected value, expected: %s, got: %s", val, resp.Kvs[0].Value)
return nil
}()
if err != nil {
diff --git a/tests/integration/v3_grpc_inflight_test.go b/tests/integration/v3_grpc_inflight_test.go
index 3d8b446e59bc..35a9ec1fd6b0 100644
--- a/tests/integration/v3_grpc_inflight_test.go
+++ b/tests/integration/v3_grpc_inflight_test.go
@@ -39,10 +39,10 @@ func TestV3MaintenanceDefragmentInflightRange(t *testing.T) {
cli := clus.RandClient()
kvc := integration.ToGRPC(cli).KV
- _, err := kvc.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), time.Second)
donec := make(chan struct{})
go func() {
@@ -51,7 +51,7 @@ func TestV3MaintenanceDefragmentInflightRange(t *testing.T) {
}()
mvc := integration.ToGRPC(cli).Maintenance
- mvc.Defragment(context.Background(), &pb.DefragmentRequest{})
+ mvc.Defragment(t.Context(), &pb.DefragmentRequest{})
cancel()
<-donec
@@ -69,10 +69,10 @@ func TestV3KVInflightRangeRequests(t *testing.T) {
cli := clus.RandClient()
kvc := integration.ToGRPC(cli).KV
- _, err := kvc.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
require.NoError(t, err)
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
reqN := 10 // use 500+ for fast machine
var wg sync.WaitGroup
diff --git a/tests/integration/v3_grpc_test.go b/tests/integration/v3_grpc_test.go
index fdf4d14b7307..36e6b77940ed 100644
--- a/tests/integration/v3_grpc_test.go
+++ b/tests/integration/v3_grpc_test.go
@@ -17,8 +17,10 @@ package integration
import (
"bytes"
"context"
+ "crypto/tls"
"errors"
"fmt"
+ "io"
"math/rand"
"os"
"reflect"
@@ -26,18 +28,23 @@ import (
"testing"
"time"
+ "github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported version
+ "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
+ "google.golang.org/protobuf/testing/protocmp"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
"go.etcd.io/etcd/client/pkg/v3/transport"
clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/server/v3/embed"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/integration"
+ gofail "go.etcd.io/gofail/runtime"
)
// TestV3PutOverwrite puts a key with the v3 api to a random Cluster member,
@@ -51,33 +58,23 @@ func TestV3PutOverwrite(t *testing.T) {
key := []byte("foo")
reqput := &pb.PutRequest{Key: key, Value: []byte("bar"), PrevKv: true}
- respput, err := kvc.Put(context.TODO(), reqput)
- if err != nil {
- t.Fatalf("couldn't put key (%v)", err)
- }
+ respput, err := kvc.Put(t.Context(), reqput)
+ require.NoErrorf(t, err, "couldn't put key")
// overwrite
reqput.Value = []byte("baz")
- respput2, err := kvc.Put(context.TODO(), reqput)
- if err != nil {
- t.Fatalf("couldn't put key (%v)", err)
- }
- if respput2.Header.Revision <= respput.Header.Revision {
- t.Fatalf("expected newer revision on overwrite, got %v <= %v",
- respput2.Header.Revision, respput.Header.Revision)
- }
+ respput2, err := kvc.Put(t.Context(), reqput)
+ require.NoErrorf(t, err, "couldn't put key")
+ require.Greaterf(t, respput2.Header.Revision, respput.Header.Revision, "expected newer revision on overwrite, got %v <= %v",
+ respput2.Header.Revision, respput.Header.Revision)
if pkv := respput2.PrevKv; pkv == nil || string(pkv.Value) != "bar" {
t.Fatalf("expected PrevKv=bar, got response %+v", respput2)
}
reqrange := &pb.RangeRequest{Key: key}
- resprange, err := kvc.Range(context.TODO(), reqrange)
- if err != nil {
- t.Fatalf("couldn't get key (%v)", err)
- }
- if len(resprange.Kvs) != 1 {
- t.Fatalf("expected 1 key, got %v", len(resprange.Kvs))
- }
+ resprange, err := kvc.Range(t.Context(), reqrange)
+ require.NoErrorf(t, err, "couldn't get key")
+ require.Lenf(t, resprange.Kvs, 1, "expected 1 key, got %v", len(resprange.Kvs))
kv := resprange.Kvs[0]
if kv.ModRevision <= kv.CreateRevision {
@@ -107,12 +104,10 @@ func TestV3PutRestart(t *testing.T) {
clus.Members[stopIdx].Stop(t)
clus.Members[stopIdx].Restart(t)
c, cerr := integration.NewClientV3(clus.Members[stopIdx])
- if cerr != nil {
- t.Fatalf("cannot create client: %v", cerr)
- }
+ require.NoErrorf(t, cerr, "cannot create client")
clus.Members[stopIdx].ServerClient = c
- ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
reqput := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
_, err := kvc.Put(ctx, reqput)
@@ -130,28 +125,21 @@ func TestV3CompactCurrentRev(t *testing.T) {
kvc := integration.ToGRPC(clus.RandClient()).KV
preq := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
for i := 0; i < 3; i++ {
- if _, err := kvc.Put(context.Background(), preq); err != nil {
- t.Fatalf("couldn't put key (%v)", err)
- }
+ _, err := kvc.Put(t.Context(), preq)
+ require.NoErrorf(t, err, "couldn't put key")
}
// get key to add to proxy cache, if any
- _, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ _, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
// compact on current revision
- _, err = kvc.Compact(context.Background(), &pb.CompactionRequest{Revision: 4})
- if err != nil {
- t.Fatalf("couldn't compact kv space (%v)", err)
- }
+ _, err = kvc.Compact(t.Context(), &pb.CompactionRequest{Revision: 4})
+ require.NoErrorf(t, err, "couldn't compact kv space")
// key still exists when linearized?
- _, err = kvc.Range(context.Background(), &pb.RangeRequest{Key: []byte("foo")})
- if err != nil {
- t.Fatalf("couldn't get key after compaction (%v)", err)
- }
+ _, err = kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
+ require.NoErrorf(t, err, "couldn't get key after compaction")
// key still exists when serialized?
- _, err = kvc.Range(context.Background(), &pb.RangeRequest{Key: []byte("foo"), Serializable: true})
- if err != nil {
- t.Fatalf("couldn't get serialized key after compaction (%v)", err)
- }
+ _, err = kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo"), Serializable: true})
+ require.NoErrorf(t, err, "couldn't get serialized key after compaction")
}
// TestV3HashKV ensures that multiple calls of HashKV on same node return same hash and compact rev.
@@ -164,11 +152,11 @@ func TestV3HashKV(t *testing.T) {
mvc := integration.ToGRPC(clus.RandClient()).Maintenance
for i := 0; i < 10; i++ {
- resp, err := kvc.Put(context.Background(), &pb.PutRequest{Key: []byte("foo"), Value: []byte(fmt.Sprintf("bar%d", i))})
+ resp, err := kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte(fmt.Sprintf("bar%d", i))})
require.NoError(t, err)
rev := resp.Header.Revision
- hresp, err := mvc.HashKV(context.Background(), &pb.HashKVRequest{Revision: 0})
+ hresp, err := mvc.HashKV(t.Context(), &pb.HashKVRequest{Revision: 0})
require.NoError(t, err)
if rev != hresp.Header.Revision {
t.Fatalf("Put rev %v != HashKV rev %v", rev, hresp.Header.Revision)
@@ -177,7 +165,7 @@ func TestV3HashKV(t *testing.T) {
prevHash := hresp.Hash
prevCompactRev := hresp.CompactRevision
for i := 0; i < 10; i++ {
- hresp, err := mvc.HashKV(context.Background(), &pb.HashKVRequest{Revision: 0})
+ hresp, err := mvc.HashKV(t.Context(), &pb.HashKVRequest{Revision: 0})
require.NoError(t, err)
if rev != hresp.Header.Revision {
t.Fatalf("Put rev %v != HashKV rev %v", rev, hresp.Header.Revision)
@@ -267,7 +255,7 @@ func TestV3TxnTooManyOps(t *testing.T) {
tt(txn)
}
- _, err := kvc.Txn(context.Background(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
if !eqErrGRPC(err, rpctypes.ErrGRPCTooManyOps) {
t.Errorf("#%d: err = %v, want %v", i, err, rpctypes.ErrGRPCTooManyOps)
}
@@ -393,7 +381,7 @@ func TestV3TxnDuplicateKeys(t *testing.T) {
}
for i, tt := range tests {
txn := &pb.TxnRequest{Success: tt.txnSuccess}
- _, err := kvc.Txn(context.Background(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
if !eqErrGRPC(err, tt.werr) {
t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
}
@@ -408,12 +396,12 @@ func TestV3TxnRevision(t *testing.T) {
kvc := integration.ToGRPC(clus.RandClient()).KV
pr := &pb.PutRequest{Key: []byte("abc"), Value: []byte("def")}
- presp, err := kvc.Put(context.TODO(), pr)
+ presp, err := kvc.Put(t.Context(), pr)
require.NoError(t, err)
txnget := &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: &pb.RangeRequest{Key: []byte("abc")}}}
txn := &pb.TxnRequest{Success: []*pb.RequestOp{txnget}}
- tresp, err := kvc.Txn(context.TODO(), txn)
+ tresp, err := kvc.Txn(t.Context(), txn)
require.NoError(t, err)
// did not update revision
@@ -423,7 +411,7 @@ func TestV3TxnRevision(t *testing.T) {
txndr := &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: &pb.DeleteRangeRequest{Key: []byte("def")}}}
txn = &pb.TxnRequest{Success: []*pb.RequestOp{txndr}}
- tresp, err = kvc.Txn(context.TODO(), txn)
+ tresp, err = kvc.Txn(t.Context(), txn)
require.NoError(t, err)
// did not update revision
@@ -433,7 +421,7 @@ func TestV3TxnRevision(t *testing.T) {
txnput := &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: &pb.PutRequest{Key: []byte("abc"), Value: []byte("123")}}}
txn = &pb.TxnRequest{Success: []*pb.RequestOp{txnput}}
- tresp, err = kvc.Txn(context.TODO(), txn)
+ tresp, err = kvc.Txn(t.Context(), txn)
require.NoError(t, err)
// updated revision
@@ -458,7 +446,7 @@ func TestV3TxnCmpHeaderRev(t *testing.T) {
go func() {
defer close(revc)
pr := &pb.PutRequest{Key: []byte("k"), Value: []byte("v")}
- presp, err := kvc.Put(context.TODO(), pr)
+ presp, err := kvc.Put(t.Context(), pr)
errCh <- err
if err != nil {
return
@@ -480,7 +468,7 @@ func TestV3TxnCmpHeaderRev(t *testing.T) {
}
txn.Compare = append(txn.Compare, cmp)
- tresp, err := kvc.Txn(context.TODO(), txn)
+ tresp, err := kvc.Txn(t.Context(), txn)
require.NoError(t, err)
prev := <-revc
@@ -505,18 +493,18 @@ func TestV3TxnRangeCompare(t *testing.T) {
// put keys, named by expected revision
for _, k := range []string{"/a/2", "/a/3", "/a/4", "/f/5"} {
- _, err := clus.Client(0).Put(context.TODO(), k, "x")
+ _, err := clus.Client(0).Put(t.Context(), k, "x")
require.NoError(t, err)
}
tests := []struct {
- cmp pb.Compare
+ cmp *pb.Compare
wSuccess bool
}{
{
// >= /a/; all create revs fit
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte{0},
Target: pb.Compare_CREATE,
@@ -527,7 +515,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// >= /a/; one create rev doesn't fit
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte{0},
Target: pb.Compare_CREATE,
@@ -538,7 +526,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// prefix /a/*; all create revs fit
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte("/a0"),
Target: pb.Compare_CREATE,
@@ -549,7 +537,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// prefix /a/*; one create rev doesn't fit
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte("/a0"),
Target: pb.Compare_CREATE,
@@ -560,7 +548,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// does not exist, does not succeed
- pb.Compare{
+ &pb.Compare{
Key: []byte("/b/"),
RangeEnd: []byte("/b0"),
Target: pb.Compare_VALUE,
@@ -571,7 +559,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// all keys are leased
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte("/a0"),
Target: pb.Compare_LEASE,
@@ -582,7 +570,7 @@ func TestV3TxnRangeCompare(t *testing.T) {
},
{
// no keys are leased
- pb.Compare{
+ &pb.Compare{
Key: []byte("/a/"),
RangeEnd: []byte("/a0"),
Target: pb.Compare_LEASE,
@@ -596,8 +584,8 @@ func TestV3TxnRangeCompare(t *testing.T) {
kvc := integration.ToGRPC(clus.Client(0)).KV
for i, tt := range tests {
txn := &pb.TxnRequest{}
- txn.Compare = append(txn.Compare, &tt.cmp)
- tresp, err := kvc.Txn(context.TODO(), txn)
+ txn.Compare = append(txn.Compare, tt.cmp)
+ tresp, err := kvc.Txn(t.Context(), txn)
require.NoError(t, err)
if tt.wSuccess != tresp.Succeeded {
t.Errorf("#%d: expected %v, got %v", i, tt.wSuccess, tresp.Succeeded)
@@ -644,13 +632,13 @@ func TestV3TxnNestedPath(t *testing.T) {
txn = nextTxn
}
- tresp, err := kvc.Txn(context.TODO(), topTxn)
+ tresp, err := kvc.Txn(t.Context(), topTxn)
require.NoError(t, err)
curTxnResp := tresp
for i := range txnPath {
if curTxnResp.Succeeded != txnPath[i] {
- t.Fatalf("expected path %+v, got response %+v", txnPath, *tresp)
+ t.Fatalf("expected path %+v, got response %s", txnPath, tresp.String())
}
curTxnResp = curTxnResp.Responses[0].Response.(*pb.ResponseOp_ResponseTxn).ResponseTxn
}
@@ -665,11 +653,17 @@ func TestV3PutIgnoreValue(t *testing.T) {
kvc := integration.ToGRPC(clus.RandClient()).KV
key, val := []byte("foo"), []byte("bar")
- putReq := pb.PutRequest{Key: key, Value: val}
+
+ newPutReq := func() *pb.PutRequest {
+ return &pb.PutRequest{
+ Key: bytes.Clone(key),
+ Value: bytes.Clone(val),
+ }
+ }
// create lease
lc := integration.ToGRPC(clus.RandClient()).Lease
- lresp, err := lc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lresp, err := lc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err)
require.Empty(t, lresp.Error)
@@ -680,9 +674,9 @@ func TestV3PutIgnoreValue(t *testing.T) {
}{
{ // put failure for non-existent key
func() error {
- preq := putReq
+ preq := newPutReq()
preq.IgnoreValue = true
- _, err := kvc.Put(context.TODO(), &preq)
+ _, err := kvc.Put(t.Context(), preq)
return err
},
rpctypes.ErrGRPCKeyNotFound,
@@ -690,14 +684,14 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // txn failure for non-existent key
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Value = nil
preq.IgnoreValue = true
txn := &pb.TxnRequest{}
txn.Success = append(txn.Success, &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{RequestPut: &preq},
+ Request: &pb.RequestOp_RequestPut{RequestPut: preq},
})
- _, err := kvc.Txn(context.TODO(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
return err
},
rpctypes.ErrGRPCKeyNotFound,
@@ -705,7 +699,7 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // put success
func() error {
- _, err := kvc.Put(context.TODO(), &putReq)
+ _, err := kvc.Put(t.Context(), newPutReq())
return err
},
nil,
@@ -713,15 +707,15 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // txn success, attach lease
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Value = nil
preq.Lease = lresp.ID
preq.IgnoreValue = true
txn := &pb.TxnRequest{}
txn.Success = append(txn.Success, &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{RequestPut: &preq},
+ Request: &pb.RequestOp_RequestPut{RequestPut: preq},
})
- _, err := kvc.Txn(context.TODO(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
return err
},
nil,
@@ -729,9 +723,9 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // non-empty value with ignore_value should error
func() error {
- preq := putReq
+ preq := newPutReq()
preq.IgnoreValue = true
- _, err := kvc.Put(context.TODO(), &preq)
+ _, err := kvc.Put(t.Context(), preq)
return err
},
rpctypes.ErrGRPCValueProvided,
@@ -739,10 +733,10 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // overwrite with previous value, ensure no prev-kv is returned and lease is detached
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Value = nil
preq.IgnoreValue = true
- presp, err := kvc.Put(context.TODO(), &preq)
+ presp, err := kvc.Put(t.Context(), preq)
if err != nil {
return err
}
@@ -756,7 +750,7 @@ func TestV3PutIgnoreValue(t *testing.T) {
},
{ // revoke lease, ensure detached key doesn't get deleted
func() error {
- _, err := lc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err := lc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
return err
},
nil,
@@ -771,7 +765,7 @@ func TestV3PutIgnoreValue(t *testing.T) {
if tt.putErr != nil {
continue
}
- rr, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: key})
+ rr, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: key})
if err != nil {
t.Fatalf("#%d: %v", i, err)
}
@@ -798,12 +792,17 @@ func TestV3PutIgnoreLease(t *testing.T) {
// create lease
lc := integration.ToGRPC(clus.RandClient()).Lease
- lresp, err := lc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lresp, err := lc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err)
require.Empty(t, lresp.Error)
key, val, val1 := []byte("zoo"), []byte("bar"), []byte("bar1")
- putReq := pb.PutRequest{Key: key, Value: val}
+ newPutReq := func() *pb.PutRequest {
+ return &pb.PutRequest{
+ Key: bytes.Clone(key),
+ Value: bytes.Clone(val),
+ }
+ }
tests := []struct {
putFunc func() error
@@ -813,9 +812,9 @@ func TestV3PutIgnoreLease(t *testing.T) {
}{
{ // put failure for non-existent key
func() error {
- preq := putReq
+ preq := newPutReq()
preq.IgnoreLease = true
- _, err := kvc.Put(context.TODO(), &preq)
+ _, err := kvc.Put(t.Context(), preq)
return err
},
rpctypes.ErrGRPCKeyNotFound,
@@ -824,13 +823,13 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // txn failure for non-existent key
func() error {
- preq := putReq
+ preq := newPutReq()
preq.IgnoreLease = true
txn := &pb.TxnRequest{}
txn.Success = append(txn.Success, &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{RequestPut: &preq},
+ Request: &pb.RequestOp_RequestPut{RequestPut: preq},
})
- _, err := kvc.Txn(context.TODO(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
return err
},
rpctypes.ErrGRPCKeyNotFound,
@@ -839,9 +838,9 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // put success
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Lease = lresp.ID
- _, err := kvc.Put(context.TODO(), &preq)
+ _, err := kvc.Put(t.Context(), preq)
return err
},
nil,
@@ -850,14 +849,14 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // txn success, modify value using 'ignore_lease' and ensure lease is not detached
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Value = val1
preq.IgnoreLease = true
txn := &pb.TxnRequest{}
txn.Success = append(txn.Success, &pb.RequestOp{
- Request: &pb.RequestOp_RequestPut{RequestPut: &preq},
+ Request: &pb.RequestOp_RequestPut{RequestPut: preq},
})
- _, err := kvc.Txn(context.TODO(), txn)
+ _, err := kvc.Txn(t.Context(), txn)
return err
},
nil,
@@ -866,10 +865,10 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // non-empty lease with ignore_lease should error
func() error {
- preq := putReq
+ preq := newPutReq()
preq.Lease = lresp.ID
preq.IgnoreLease = true
- _, err := kvc.Put(context.TODO(), &preq)
+ _, err := kvc.Put(t.Context(), preq)
return err
},
rpctypes.ErrGRPCLeaseProvided,
@@ -878,7 +877,7 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // overwrite with previous value, ensure no prev-kv is returned and lease is detached
func() error {
- presp, err := kvc.Put(context.TODO(), &putReq)
+ presp, err := kvc.Put(t.Context(), newPutReq())
if err != nil {
return err
}
@@ -893,7 +892,7 @@ func TestV3PutIgnoreLease(t *testing.T) {
},
{ // revoke lease, ensure detached key doesn't get deleted
func() error {
- _, err := lc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err := lc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
return err
},
nil,
@@ -909,7 +908,7 @@ func TestV3PutIgnoreLease(t *testing.T) {
if tt.putErr != nil {
continue
}
- rr, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: key})
+ rr, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: key})
if err != nil {
t.Fatalf("#%d: %v", i, err)
}
@@ -937,7 +936,7 @@ func TestV3PutMissingLease(t *testing.T) {
tests := []func(){
// put case
func() {
- if presp, err := kvc.Put(context.TODO(), preq); err == nil {
+ if presp, err := kvc.Put(t.Context(), preq); err == nil {
t.Errorf("succeeded put key. req: %v. resp: %v", preq, presp)
}
},
@@ -949,7 +948,7 @@ func TestV3PutMissingLease(t *testing.T) {
RequestPut: preq,
},
})
- if tresp, err := kvc.Txn(context.TODO(), txn); err == nil {
+ if tresp, err := kvc.Txn(t.Context(), txn); err == nil {
t.Errorf("succeeded txn success. req: %v. resp: %v", txn, tresp)
}
},
@@ -967,7 +966,7 @@ func TestV3PutMissingLease(t *testing.T) {
Key: []byte("bar"),
}
txn.Compare = append(txn.Compare, cmp)
- if tresp, err := kvc.Txn(context.TODO(), txn); err == nil {
+ if tresp, err := kvc.Txn(t.Context(), txn); err == nil {
t.Errorf("succeeded txn failure. req: %v. resp: %v", txn, tresp)
}
},
@@ -985,7 +984,7 @@ func TestV3PutMissingLease(t *testing.T) {
RequestPut: preq,
},
})
- if tresp, err := kvc.Txn(context.TODO(), txn); err != nil {
+ if tresp, err := kvc.Txn(t.Context(), txn); err != nil {
t.Errorf("failed good txn. req: %v. resp: %v", txn, tresp)
}
},
@@ -995,7 +994,7 @@ func TestV3PutMissingLease(t *testing.T) {
f()
// key shouldn't have been stored
rreq := &pb.RangeRequest{Key: key}
- rresp, err := kvc.Range(context.TODO(), rreq)
+ rresp, err := kvc.Range(t.Context(), rreq)
if err != nil {
t.Errorf("#%d. could not rangereq (%v)", i, err)
} else if len(rresp.Kvs) != 0 {
@@ -1078,7 +1077,7 @@ func TestV3DeleteRange(t *testing.T) {
ks := tt.keySet
for j := range ks {
reqput := &pb.PutRequest{Key: []byte(ks[j]), Value: []byte{}}
- _, err := kvc.Put(context.TODO(), reqput)
+ _, err := kvc.Put(t.Context(), reqput)
if err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
@@ -1089,7 +1088,7 @@ func TestV3DeleteRange(t *testing.T) {
RangeEnd: []byte(tt.end),
PrevKv: tt.prevKV,
}
- dresp, err := kvc.DeleteRange(context.TODO(), dreq)
+ dresp, err := kvc.DeleteRange(t.Context(), dreq)
if err != nil {
t.Fatalf("couldn't delete range on test %d (%v)", i, err)
}
@@ -1103,7 +1102,7 @@ func TestV3DeleteRange(t *testing.T) {
}
rreq := &pb.RangeRequest{Key: []byte{0x0}, RangeEnd: []byte{0xff}}
- rresp, err := kvc.Range(context.TODO(), rreq)
+ rresp, err := kvc.Range(t.Context(), rreq)
if err != nil {
t.Errorf("couldn't get range on test %v (%v)", i, err)
}
@@ -1133,13 +1132,13 @@ func TestV3TxnInvalidRange(t *testing.T) {
preq := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
for i := 0; i < 3; i++ {
- _, err := kvc.Put(context.Background(), preq)
+ _, err := kvc.Put(t.Context(), preq)
if err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
}
- _, err := kvc.Compact(context.Background(), &pb.CompactionRequest{Revision: 2})
+ _, err := kvc.Compact(t.Context(), &pb.CompactionRequest{Revision: 2})
if err != nil {
t.Fatalf("couldn't compact kv space (%v)", err)
}
@@ -1159,14 +1158,14 @@ func TestV3TxnInvalidRange(t *testing.T) {
},
})
- if _, err := kvc.Txn(context.TODO(), txn); !eqErrGRPC(err, rpctypes.ErrGRPCFutureRev) {
+ if _, err := kvc.Txn(t.Context(), txn); !eqErrGRPC(err, rpctypes.ErrGRPCFutureRev) {
t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCFutureRev)
}
// compacted rev
tv, _ := txn.Success[1].Request.(*pb.RequestOp_RequestRange)
tv.RequestRange.Revision = 1
- if _, err := kvc.Txn(context.TODO(), txn); !eqErrGRPC(err, rpctypes.ErrGRPCCompacted) {
+ if _, err := kvc.Txn(t.Context(), txn); !eqErrGRPC(err, rpctypes.ErrGRPCCompacted) {
t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCCompacted)
}
}
@@ -1183,7 +1182,7 @@ func TestV3TooLargeRequest(t *testing.T) {
largeV := make([]byte, 2*1024*1024)
preq := &pb.PutRequest{Key: []byte("foo"), Value: largeV}
- _, err := kvc.Put(context.Background(), preq)
+ _, err := kvc.Put(t.Context(), preq)
if !eqErrGRPC(err, rpctypes.ErrGRPCRequestTooLarge) {
t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCRequestTooLarge)
}
@@ -1202,13 +1201,13 @@ func TestV3Hash(t *testing.T) {
preq := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
for i := 0; i < 3; i++ {
- _, err := kvc.Put(context.Background(), preq)
+ _, err := kvc.Put(t.Context(), preq)
if err != nil {
t.Fatalf("couldn't put key (%v)", err)
}
}
- resp, err := m.Hash(context.Background(), &pb.HashRequest{})
+ resp, err := m.Hash(t.Context(), &pb.HashRequest{})
if err != nil || resp.Hash == 0 {
t.Fatalf("couldn't hash (%v, hash %d)", err, resp.Hash)
}
@@ -1221,7 +1220,7 @@ func TestV3HashRestart(t *testing.T) {
defer clus.Terminate(t)
cli := clus.RandClient()
- resp, err := integration.ToGRPC(cli).Maintenance.Hash(context.Background(), &pb.HashRequest{})
+ resp, err := integration.ToGRPC(cli).Maintenance.Hash(t.Context(), &pb.HashRequest{})
if err != nil || resp.Hash == 0 {
t.Fatalf("couldn't hash (%v, hash %d)", err, resp.Hash)
}
@@ -1234,7 +1233,7 @@ func TestV3HashRestart(t *testing.T) {
waitForRestart(t, kvc)
cli = clus.RandClient()
- resp, err = integration.ToGRPC(cli).Maintenance.Hash(context.Background(), &pb.HashRequest{})
+ resp, err = integration.ToGRPC(cli).Maintenance.Hash(t.Context(), &pb.HashRequest{})
if err != nil || resp.Hash == 0 {
t.Fatalf("couldn't hash (%v, hash %d)", err, resp.Hash)
}
@@ -1265,12 +1264,12 @@ func TestV3StorageQuotaAPI(t *testing.T) {
// test small put that fits in quota
smallbuf := make([]byte, 512)
- _, err := kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: smallbuf})
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{Key: key, Value: smallbuf})
require.NoError(t, err)
// test big put
bigbuf := make([]byte, quotasize)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: key, Value: bigbuf})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: key, Value: bigbuf})
if !eqErrGRPC(err, rpctypes.ErrGRPCNoSpace) {
t.Fatalf("big put got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
}
@@ -1286,7 +1285,7 @@ func TestV3StorageQuotaAPI(t *testing.T) {
}
txnreq := &pb.TxnRequest{}
txnreq.Success = append(txnreq.Success, puttxn)
- _, txnerr := kvc.Txn(context.TODO(), txnreq)
+ _, txnerr := kvc.Txn(t.Context(), txnreq)
if !eqErrGRPC(txnerr, rpctypes.ErrGRPCNoSpace) {
t.Fatalf("big txn got %v, expected %v", err, rpctypes.ErrGRPCNoSpace)
}
@@ -1298,16 +1297,21 @@ func TestV3RangeRequest(t *testing.T) {
name string
putKeys []string
- reqs []pb.RangeRequest
+ reqs []*pb.RangeRequest
wresps [][]string
wmores []bool
wcounts []int64
+
+ // streamUnsupported[j] marks request reqs[j] as using features that
+ // RangeStream intentionally does not support (non-key-ascending sort,
+ // min/max mod/create revision filters). Must match len(reqs).
+ streamUnsupported []bool
}{
{
"single key",
[]string{"foo", "bar"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
// exists
{Key: []byte("foo")},
// doesn't exist
@@ -1320,11 +1324,12 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{false, false},
[]int64{1, 0},
+ []bool{false, false},
},
{
"multi-key",
[]string{"a", "b", "c", "d", "e"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
// all in range
{Key: []byte("a"), RangeEnd: []byte("z")},
// [b, d)
@@ -1349,11 +1354,12 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{false, false, false, false, false, false},
[]int64{5, 2, 0, 0, 0, 5},
+ []bool{false, false, false, false, false, false},
},
{
"revision",
[]string{"a", "b", "c", "d", "e"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 0},
{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 1},
{Key: []byte("a"), RangeEnd: []byte("z"), Revision: 2},
@@ -1368,11 +1374,12 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{false, false, false, false},
[]int64{5, 0, 1, 2},
+ []bool{false, false, false, false},
},
{
"limit",
[]string{"a", "b", "c"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
// more
{Key: []byte("a"), RangeEnd: []byte("z"), Limit: 1},
// half
@@ -1391,11 +1398,31 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{true, true, false, false},
[]int64{3, 3, 3, 3},
+ []bool{false, false, false, false},
+ },
+ {
+ "count only",
+ []string{"a", "b", "c"},
+ []*pb.RangeRequest{
+ // all match
+ {Key: []byte("a"), RangeEnd: []byte("z"), CountOnly: true},
+ // single-key match
+ {Key: []byte("b"), RangeEnd: []byte("c"), CountOnly: true},
+ // no match
+ {Key: []byte("x"), RangeEnd: []byte("z"), CountOnly: true},
+ // CountOnly with Limit: Limit must not truncate the count.
+ {Key: []byte("a"), RangeEnd: []byte("z"), Limit: 1, CountOnly: true},
+ },
+
+ [][]string{{}, {}, {}, {}},
+ []bool{false, false, false, false},
+ []int64{3, 1, 0, 3},
+ []bool{false, false, false, false},
},
{
"sort",
[]string{"b", "a", "c", "d", "c"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
{
Key: []byte("a"), RangeEnd: []byte("z"),
Limit: 1,
@@ -1444,11 +1471,13 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{true, true, true, true, false, false},
[]int64{4, 4, 4, 4, 0, 4},
+ // Only ASCEND+KEY (index 0) and SortOrder_NONE (index 5) are supported by RangeStream.
+ []bool{false, true, true, true, true, false},
},
{
"min/max mod rev",
[]string{"rev2", "rev3", "rev4", "rev5", "rev6"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
{
Key: []byte{0}, RangeEnd: []byte{0},
MinModRevision: 3,
@@ -1476,11 +1505,12 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{false, false, false, false},
[]int64{5, 5, 5, 5},
+ []bool{true, true, true, true},
},
{
"min/max create rev",
[]string{"rev2", "rev3", "rev2", "rev2", "rev6", "rev3"},
- []pb.RangeRequest{
+ []*pb.RangeRequest{
{
Key: []byte{0}, RangeEnd: []byte{0},
MinCreateRevision: 3,
@@ -1508,6 +1538,7 @@ func TestV3RangeRequest(t *testing.T) {
},
[]bool{false, false, false, false},
[]int64{3, 3, 3, 3},
+ []bool{true, true, true, true},
},
}
@@ -1518,18 +1549,24 @@ func TestV3RangeRequest(t *testing.T) {
for _, k := range tt.putKeys {
kvc := integration.ToGRPC(clus.RandClient()).KV
req := &pb.PutRequest{Key: []byte(k), Value: []byte("bar")}
- if _, err := kvc.Put(context.TODO(), req); err != nil {
+ if _, err := kvc.Put(t.Context(), req); err != nil {
t.Fatalf("#%d: couldn't put key (%v)", i, err)
}
}
for j, req := range tt.reqs {
kvc := integration.ToGRPC(clus.RandClient()).KV
- resp, err := kvc.Range(context.TODO(), &req)
+ resp, err := kvc.Range(t.Context(), req)
if err != nil {
t.Errorf("#%d.%d: Range error: %v", i, j, err)
continue
}
+ if !integration.ThroughProxy && !tt.streamUnsupported[j] {
+ got := rangeStream(t, kvc, req)
+ require.Emptyf(t, cmp.Diff(resp, got, protocmp.Transform()),
+ "RangeStream response must match Range response")
+ }
+
if len(resp.Kvs) != len(tt.wresps[j]) {
t.Errorf("#%d.%d: bad len(resp.Kvs). got = %d, want = %d, ", i, j, len(resp.Kvs), len(tt.wresps[j]))
continue
@@ -1555,6 +1592,322 @@ func TestV3RangeRequest(t *testing.T) {
}
}
+// rangeStream calls RangeStream and returns the merged response.
+func rangeStream(t *testing.T, kvc pb.KVClient, req *pb.RangeRequest) *pb.RangeResponse {
+ t.Helper()
+
+ stream, err := kvc.RangeStream(t.Context(), req)
+ require.NoError(t, err)
+
+ got := &pb.RangeResponse{}
+ for {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ break
+ }
+ require.NoError(t, rerr)
+ proto.Merge(got, chunk.RangeResponse)
+ }
+ return got
+}
+
+// TestV3RangeStreamCount verifies Count semantics on the last streamed chunk,
+// including the case where the stream truncates at Limit with more matching
+// keys pending (exercises the CountOnly fallback query at the pinned revision).
+func TestV3RangeStreamCount(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("RangeStream is not supported by the gRPC proxy")
+ }
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ kvc := integration.ToGRPC(clus.RandClient()).KV
+ const nKeys = 25
+ for i := 0; i < nKeys; i++ {
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte(fmt.Sprintf("k%02d", i)),
+ Value: []byte("v"),
+ })
+ require.NoError(t, err)
+ }
+
+ // The server-side chunker starts at Limit=10 and adapts from there, so
+ // nKeys=25 reliably produces multi-chunk responses for unlimited/large
+ // limits, while small limits fit in the first chunk.
+ tests := []struct {
+ name string
+ limit int64
+ wantCount int64
+ wantMore bool
+ wantKeys int
+ wantMinRecv int
+ }{
+ {
+ name: "unlimited exhausts with running count",
+ limit: 0,
+ wantCount: nKeys,
+ wantMore: false,
+ wantKeys: nKeys,
+ wantMinRecv: 2,
+ },
+ {
+ name: "limit below total truncates with fallback count",
+ limit: 5,
+ wantCount: nKeys,
+ wantMore: true,
+ wantKeys: 5,
+ wantMinRecv: 1,
+ },
+ {
+ name: "limit equal to total, no truncation",
+ limit: nKeys,
+ wantCount: nKeys,
+ wantMore: false,
+ wantKeys: nKeys,
+ wantMinRecv: 2,
+ },
+ {
+ name: "limit above total, exhausts early",
+ limit: 100,
+ wantCount: nKeys,
+ wantMore: false,
+ wantKeys: nKeys,
+ wantMinRecv: 2,
+ },
+ {
+ name: "limit at first-chunk boundary with fallback count",
+ limit: 10,
+ wantCount: nKeys,
+ wantMore: true,
+ wantKeys: 10,
+ wantMinRecv: 1,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ stream, err := kvc.RangeStream(t.Context(), &pb.RangeRequest{
+ Key: []byte("k00"),
+ RangeEnd: []byte("l"),
+ Limit: tt.limit,
+ })
+ require.NoError(t, err)
+
+ merged := &pb.RangeResponse{}
+ recvs := 0
+ for {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ break
+ }
+ require.NoError(t, rerr)
+ recvs++
+ proto.Merge(merged, chunk.RangeResponse)
+ }
+ require.Equalf(t, tt.wantCount, merged.Count, "Count")
+ require.Equalf(t, tt.wantMore, merged.More, "More")
+ require.Lenf(t, merged.Kvs, tt.wantKeys, "kv count")
+ require.GreaterOrEqualf(t, recvs, tt.wantMinRecv, "chunk count")
+ })
+ }
+}
+
+// TestV3RangeStreamPartialThenCompacted verifies that once the stream has
+// emitted partial results at a pinned revision, a compaction past that
+// revision causes the next chunk to surface ErrCompacted instead of silently
+// returning inconsistent data.
+func TestV3RangeStreamPartialThenCompacted(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("RangeStream is not supported by the gRPC proxy")
+ }
+
+ integration.BeforeTest(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ kvc := integration.ToGRPC(clus.RandClient()).KV
+ value := []byte(strings.Repeat("x", 10*1024))
+ for i := 0; i < 1000; i++ {
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte(fmt.Sprintf("stream-%04d", i)),
+ Value: value,
+ })
+ require.NoError(t, err)
+ }
+
+ stream, err := kvc.RangeStream(t.Context(), &pb.RangeRequest{
+ Key: []byte("stream-"),
+ RangeEnd: []byte("stream."),
+ })
+ require.NoError(t, err)
+
+ received := 0
+ for received < 10 {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ t.Fatalf("stream ended before the test could compact; received %d keys", received)
+ }
+ require.NoError(t, rerr)
+ received += len(chunk.GetRangeResponse().GetKvs())
+ }
+
+ advance, err := kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte("zz-advance-revision"),
+ Value: []byte("x"),
+ })
+ require.NoError(t, err)
+
+ _, err = kvc.Compact(t.Context(), &pb.CompactionRequest{
+ Revision: advance.Header.Revision,
+ Physical: true,
+ })
+ require.NoError(t, err)
+
+ for {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ t.Fatalf("expected compacted error after receiving partial keys; received %d keys", received)
+ }
+
+ if rerr != nil {
+ require.Truef(t, eqErrGRPC(rerr, rpctypes.ErrGRPCCompacted), "got %v, expected %v", rerr, rpctypes.ErrGRPCCompacted)
+ require.GreaterOrEqual(t, received, 10)
+ return
+ }
+ received += len(chunk.GetRangeResponse().GetKvs())
+ }
+}
+
+func TestV3RangeStreamWriteBetweenChunks(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("RangeStream is not supported by the gRPC proxy")
+ }
+
+ integration.BeforeTest(t)
+ integration.SkipIfNoGoFail(t)
+ clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
+ defer clus.Terminate(t)
+
+ kvc := integration.ToGRPC(clus.RandClient()).KV
+
+ // Relies on an implementation detail that n > 10 yields multiple chunks.
+ const nKeys = 25
+ var pinnedRev int64
+ for i := 0; i < nKeys; i++ {
+ resp, err := kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte(fmt.Sprintf("k%02d", i)),
+ Value: []byte("v"),
+ })
+ require.NoError(t, err)
+ pinnedRev = resp.Header.Revision
+ }
+
+ // Sleep between chunks to prevent gRPC queueing.
+ require.NoError(t, gofail.Enable("beforeRangeStreamChunk", `sleep("1s")`))
+ defer func() { require.NoError(t, gofail.Disable("beforeRangeStreamChunk")) }()
+
+ stream, err := kvc.RangeStream(t.Context(), &pb.RangeRequest{
+ Key: []byte("k00"),
+ RangeEnd: []byte("l"),
+ })
+ require.NoError(t, err)
+
+ // Recv before Put so the Put is guaranteed to be after the revision pin.
+ first, err := stream.Recv()
+ require.NoError(t, err)
+ require.NotEmpty(t, first.GetRangeResponse().GetKvs())
+
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte("k99-new"),
+ Value: []byte("v"),
+ })
+ require.NoError(t, err)
+
+ merged := &pb.RangeResponse{}
+ proto.Merge(merged, first.RangeResponse)
+ chunks := 1
+ for {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ break
+ }
+ require.NoError(t, rerr)
+ chunks++
+ proto.Merge(merged, chunk.RangeResponse)
+ }
+
+ require.GreaterOrEqualf(t, chunks, 2, "expected multi-chunk stream, got %d", chunks)
+ require.Lenf(t, merged.Kvs, nKeys, "stream must return only the keys that existed at pinned revision")
+ for i, kv := range merged.Kvs {
+ require.Equal(t, fmt.Sprintf("k%02d", i), string(kv.Key))
+ require.Equal(t, "v", string(kv.Value))
+ }
+ require.Equalf(t, pinnedRev, merged.Header.Revision, "stream header revision must equal the latest revision at stream start")
+ require.Equalf(t, int64(nKeys), merged.Count, "stream Count must reflect the pinned revision")
+}
+
+// TestV3RangeStreamLargeValues verifies the stream progresses and emits all
+// KVs when individual stored values exceed the chunk target.
+func TestV3RangeStreamLargeValues(t *testing.T) {
+ if integration.ThroughProxy {
+ t.Skip("RangeStream is not supported by the gRPC proxy")
+ }
+ integration.BeforeTest(t)
+
+ const (
+ valueSize = 4 * 1024 * 1024
+ nKeys = 20
+ )
+ clus := integration.NewCluster(t, &integration.ClusterConfig{
+ Size: 1,
+ MaxRequestBytes: 8 * 1024 * 1024,
+ })
+ defer clus.Terminate(t)
+
+ kvc := integration.ToGRPC(clus.Client(0)).KV
+ value := make([]byte, valueSize)
+ for i := 0; i < nKeys; i++ {
+ _, err := kvc.Put(t.Context(), &pb.PutRequest{
+ Key: []byte(fmt.Sprintf("big-%02d", i)),
+ Value: value,
+ })
+ require.NoError(t, err)
+ }
+
+ m := clus.Members[0]
+ m.Stop(t)
+ m.MaxRequestBytes = embed.DefaultMaxRequestBytes
+ require.Lessf(t, int(m.MaxRequestBytes), valueSize, "valueSize must exceed MaxRequestBytes to exercise large-value path")
+ require.NoError(t, m.Restart(t))
+ clus.WaitMembersForLeader(t, clus.Members)
+ kvc = integration.ToGRPC(clus.Client(0)).KV
+ waitForRestart(t, kvc)
+
+ stream, err := kvc.RangeStream(t.Context(), &pb.RangeRequest{
+ Key: []byte("big-"),
+ RangeEnd: []byte("big."),
+ }, grpc.MaxCallRecvMsgSize(nKeys*valueSize+512*1024))
+ require.NoError(t, err)
+
+ var kvs, recvs int
+ for {
+ chunk, rerr := stream.Recv()
+ if errors.Is(rerr, io.EOF) {
+ break
+ }
+ require.NoError(t, rerr)
+ recvs++
+ for j, kv := range chunk.GetRangeResponse().GetKvs() {
+ require.Equal(t, fmt.Sprintf("big-%02d", kvs+j), string(kv.Key))
+ require.Equal(t, value, kv.Value)
+ }
+ kvs += len(chunk.GetRangeResponse().GetKvs())
+ }
+ require.Equalf(t, nKeys, kvs, "kv count")
+ require.GreaterOrEqualf(t, recvs, 2, "expected multi-chunk stream for %d-byte values", valueSize)
+}
+
// TestTLSGRPCRejectInsecureClient checks that connection is rejected if server is TLS but not client.
func TestTLSGRPCRejectInsecureClient(t *testing.T) {
integration.BeforeTest(t)
@@ -1577,7 +1930,7 @@ func TestTLSGRPCRejectInsecureClient(t *testing.T) {
donec := make(chan error, 1)
go func() {
- ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
reqput := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
_, perr := integration.ToGRPC(client).KV.Put(ctx, reqput)
cancel()
@@ -1597,12 +1950,15 @@ func TestTLSGRPCRejectSecureClient(t *testing.T) {
defer clus.Terminate(t)
clus.Members[0].ClientTLSInfo = &integration.TestTLSInfo
- clus.Members[0].DialOptions = []grpc.DialOption{grpc.WithBlock()}
clus.Members[0].GRPCURL = strings.Replace(clus.Members[0].GRPCURL, "http://", "https://", 1)
client, err := integration.NewClientV3(clus.Members[0])
- if client != nil || err == nil {
- client.Close()
- t.Fatalf("expected no client")
+ require.NoError(t, err)
+ defer client.Close()
+ ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
+ _, err = client.Status(ctx, client.Endpoints()[0])
+ cancel()
+ if err == nil {
+ t.Fatalf("expected error")
} else if !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("unexpected error (%v)", err)
}
@@ -1622,7 +1978,7 @@ func TestTLSGRPCAcceptSecureAll(t *testing.T) {
defer client.Close()
reqput := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
- if _, err := integration.ToGRPC(client).KV.Put(context.TODO(), reqput); err != nil {
+ if _, err := integration.ToGRPC(client).KV.Put(t.Context(), reqput); err != nil {
t.Fatalf("unexpected error on put over tls (%v)", err)
}
}
@@ -1731,11 +2087,15 @@ func testTLSReload(
})
defer clus.Terminate(t)
- // 3. concurrent client dialing while certs become expired
+ // 3. concurrent client creation while certs become expired
errc := make(chan error, 1)
+ donec := make(chan struct{})
+ var tlsInvalid *tls.Config
go func() {
+ defer close(donec)
for {
- cc, err := tlsInfo.ClientConfig()
+ var err error
+ tlsInvalid, err = tlsInfo.ClientConfig()
if err != nil {
// errors in 'go/src/crypto/tls/tls.go'
// tls: private key does not match public key
@@ -1746,47 +2106,69 @@ func testTLSReload(
continue
}
cli, cerr := integration.NewClient(t, clientv3.Config{
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: time.Second,
- TLS: cc,
+ TLS: tlsInvalid,
})
if cerr != nil {
errc <- cerr
- return
}
cli.Close()
+ return
}
}()
// 4. replace certs with expired ones
replaceFunc()
- // 5. expect dial time-out when loading expired certs
select {
case gerr := <-errc:
- if !errors.Is(gerr, context.DeadlineExceeded) {
- t.Fatalf("expected %v, got %v", context.DeadlineExceeded, gerr)
+ t.Fatalf("expected no error, got %v", gerr)
+ case <-time.After(2 * time.Second):
+ }
+ <-donec
+
+ readTest := func(tlsCfg *tls.Config, expectedErr error) {
+ cli, cerr := integration.NewClient(t, clientv3.Config{
+ Endpoints: []string{clus.Members[0].GRPCURL},
+ DialTimeout: time.Second,
+ TLS: tlsCfg,
+ })
+ require.NoError(t, cerr)
+ defer func() {
+ require.NoError(t, cli.Close())
+ }()
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ _, rerr := cli.Get(ctx, "foo")
+ cancel()
+ if expectedErr != nil {
+ require.ErrorIs(t, rerr, expectedErr)
+ } else {
+ require.NoError(t, rerr)
}
- case <-time.After(5 * time.Second):
- t.Fatal("failed to receive dial timeout error")
}
+ // 5. expect read request timed out when loading expired certs
+ readTest(tlsInvalid, context.DeadlineExceeded)
+
// 6. replace expired certs back with valid ones
revertFunc()
- // 7. new requests should trigger listener to reload valid certs
- tls, terr := tlsInfo.ClientConfig()
+ tlsValid, terr := tlsInfo.ClientConfig()
require.NoError(t, terr)
cl, cerr := integration.NewClient(t, clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: 5 * time.Second,
- TLS: tls,
+ TLS: tlsValid,
})
if cerr != nil {
t.Fatalf("expected no error, got %v", cerr)
}
- cl.Close()
+ require.NoError(t, cl.Close())
+
+ // 7. new requests should trigger listener to reload valid certs
+ readTest(tlsValid, nil)
}
func TestGRPCRequireLeader(t *testing.T) {
@@ -1808,7 +2190,7 @@ func TestGRPCRequireLeader(t *testing.T) {
time.Sleep(time.Duration(3*integration.ElectionTicks) * config.TickDuration)
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
- ctx := metadata.NewOutgoingContext(context.Background(), md)
+ ctx := metadata.NewOutgoingContext(t.Context(), md)
reqput := &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")}
if _, err := integration.ToGRPC(client).KV.Put(ctx, reqput); rpctypes.ErrorDesc(err) != rpctypes.ErrNoLeader.Error() {
t.Errorf("err = %v, want %v", err, rpctypes.ErrNoLeader)
@@ -1829,7 +2211,7 @@ func TestGRPCStreamRequireLeader(t *testing.T) {
wAPI := integration.ToGRPC(client).Watch
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
- ctx := metadata.NewOutgoingContext(context.Background(), md)
+ ctx := metadata.NewOutgoingContext(t.Context(), md)
wStream, err := wAPI.Watch(ctx)
if err != nil {
t.Fatalf("wAPI.Watch error: %v", err)
@@ -1896,7 +2278,7 @@ func TestV3LargeRequests(t *testing.T) {
defer clus.Terminate(t)
kvcli := integration.ToGRPC(clus.Client(0)).KV
reqput := &pb.PutRequest{Key: []byte("foo"), Value: make([]byte, test.valueSize)}
- _, err := kvcli.Put(context.TODO(), reqput)
+ _, err := kvcli.Put(t.Context(), reqput)
if !eqErrGRPC(err, test.expectError) {
t.Errorf("#%d: expected error %v, got %v", i, test.expectError, err)
}
@@ -1905,7 +2287,7 @@ func TestV3LargeRequests(t *testing.T) {
if test.expectError == nil {
reqget := &pb.RangeRequest{Key: []byte("foo")}
// limit receive call size with original value + gRPC overhead bytes
- _, err = kvcli.Range(context.TODO(), reqget, grpc.MaxCallRecvMsgSize(test.valueSize+512*1024))
+ _, err = kvcli.Range(t.Context(), reqget, grpc.MaxCallRecvMsgSize(test.valueSize+512*1024))
if err != nil {
t.Errorf("#%d: range expected no error, got %v", i, err)
}
@@ -1957,7 +2339,7 @@ func TestV3AdditionalGRPCOptions(t *testing.T) {
defer clus.Terminate(t)
kvcli := integration.ToGRPC(clus.Client(0)).KV
reqput := &pb.PutRequest{Key: []byte("foo"), Value: make([]byte, test.valueSize)}
- if _, err := kvcli.Put(context.TODO(), reqput); err != nil {
+ if _, err := kvcli.Put(t.Context(), reqput); err != nil {
var etcdErr rpctypes.EtcdError
if errors.As(err, &etcdErr) {
if err.Error() != status.Convert(test.expectError).Message() {
@@ -1971,7 +2353,7 @@ func TestV3AdditionalGRPCOptions(t *testing.T) {
if test.expectError == nil {
reqget := &pb.RangeRequest{Key: []byte("foo")}
// limit receive call size with original value + gRPC overhead bytes
- _, err := kvcli.Range(context.TODO(), reqget, grpc.MaxCallRecvMsgSize(test.valueSize+512*1024))
+ _, err := kvcli.Range(t.Context(), reqget, grpc.MaxCallRecvMsgSize(test.valueSize+512*1024))
if err != nil {
t.Errorf("range expected no error, got %v", err)
}
@@ -1993,7 +2375,7 @@ func waitForRestart(t *testing.T, kvc pb.KVClient) {
// TODO: Remove retry loop once the new grpc load balancer provides retry.
var err error
for i := 0; i < 10; i++ {
- if _, err = kvc.Range(context.TODO(), req, grpc.WaitForReady(true)); err != nil {
+ if _, err = kvc.Range(t.Context(), req, grpc.WaitForReady(true)); err != nil {
if status, ok := status.FromError(err); ok && status.Code() == codes.Unavailable {
time.Sleep(time.Millisecond * 250)
} else {
diff --git a/tests/integration/v3_kv_test.go b/tests/integration/v3_kv_test.go
index 1a5e7ce5ea2d..7b6b9644e1b6 100644
--- a/tests/integration/v3_kv_test.go
+++ b/tests/integration/v3_kv_test.go
@@ -15,7 +15,6 @@
package integration
import (
- "context"
"testing"
"github.com/stretchr/testify/require"
@@ -34,15 +33,15 @@ func TestKVWithEmptyValue(t *testing.T) {
client := clus.RandClient()
- _, err := client.Put(context.Background(), "my-namespace/foobar", "data")
+ _, err := client.Put(t.Context(), "my-namespace/foobar", "data")
require.NoError(t, err)
- _, err = client.Put(context.Background(), "my-namespace/foobar1", "data")
+ _, err = client.Put(t.Context(), "my-namespace/foobar1", "data")
require.NoError(t, err)
- _, err = client.Put(context.Background(), "namespace/foobar1", "data")
+ _, err = client.Put(t.Context(), "namespace/foobar1", "data")
require.NoError(t, err)
// Range over all keys.
- resp, err := client.Get(context.Background(), "", clientv3.WithFromKey())
+ resp, err := client.Get(t.Context(), "", clientv3.WithFromKey())
require.NoError(t, err)
for _, kv := range resp.Kvs {
t.Log(string(kv.Key), "=", string(kv.Value))
@@ -50,18 +49,18 @@ func TestKVWithEmptyValue(t *testing.T) {
// Range over all keys in a namespace.
client.KV = namespace.NewKV(client.KV, "my-namespace/")
- resp, err = client.Get(context.Background(), "", clientv3.WithFromKey())
+ resp, err = client.Get(t.Context(), "", clientv3.WithFromKey())
require.NoError(t, err)
for _, kv := range resp.Kvs {
t.Log(string(kv.Key), "=", string(kv.Value))
}
// Remove all keys without WithFromKey/WithPrefix func
- _, err = client.Delete(context.Background(), "")
+ _, err = client.Delete(t.Context(), "")
// fatal error duo to without WithFromKey/WithPrefix func called.
require.Error(t, err)
- respDel, err := client.Delete(context.Background(), "", clientv3.WithFromKey())
+ respDel, err := client.Delete(t.Context(), "", clientv3.WithFromKey())
// fatal error duo to with WithFromKey/WithPrefix func called.
require.NoError(t, err)
t.Logf("delete keys:%d", respDel.Deleted)
diff --git a/tests/integration/v3_leadership_test.go b/tests/integration/v3_leadership_test.go
index ea2b730c6189..9e996e787ed7 100644
--- a/tests/integration/v3_leadership_test.go
+++ b/tests/integration/v3_leadership_test.go
@@ -63,7 +63,7 @@ func testMoveLeader(t *testing.T, auto bool) {
require.NoError(t, err)
} else {
mvc := integration.ToGRPC(clus.Client(oldLeadIdx)).Maintenance
- _, err := mvc.MoveLeader(context.TODO(), &pb.MoveLeaderRequest{TargetID: target})
+ _, err := mvc.MoveLeader(t.Context(), &pb.MoveLeaderRequest{TargetID: target})
require.NoError(t, err)
}
@@ -108,7 +108,7 @@ func TestMoveLeaderError(t *testing.T) {
target := uint64(clus.Members[(oldLeadIdx+2)%3].Server.MemberID())
mvc := integration.ToGRPC(clus.Client(followerIdx)).Maintenance
- _, err := mvc.MoveLeader(context.TODO(), &pb.MoveLeaderRequest{TargetID: target})
+ _, err := mvc.MoveLeader(t.Context(), &pb.MoveLeaderRequest{TargetID: target})
if !eqErrGRPC(err, rpctypes.ErrGRPCNotLeader) {
t.Errorf("err = %v, want %v", err, rpctypes.ErrGRPCNotLeader)
}
@@ -136,7 +136,7 @@ func TestMoveLeaderToLearnerError(t *testing.T) {
learnerID := learners[0].ID
leaderIdx := clus.WaitLeader(t)
cli := clus.Client(leaderIdx)
- _, err = cli.MoveLeader(context.Background(), learnerID)
+ _, err = cli.MoveLeader(t.Context(), learnerID)
if err == nil {
t.Fatalf("expecting leader transfer to learner to fail, got no error")
}
@@ -183,7 +183,7 @@ func TestTransferLeadershipWithLearner(t *testing.T) {
func TestFirstCommitNotification(t *testing.T) {
integration.BeforeTest(t)
- ctx := context.Background()
+ ctx := t.Context()
clusterSize := 3
cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: clusterSize})
defer cluster.Terminate(t)
@@ -199,7 +199,7 @@ func TestFirstCommitNotification(t *testing.T) {
notifiers[i] = clusterMember.Server.FirstCommitInTermNotify()
}
- _, err := oldLeaderClient.MoveLeader(context.Background(), newLeaderID)
+ _, err := oldLeaderClient.MoveLeader(t.Context(), newLeaderID)
if err != nil {
t.Errorf("got error during leadership transfer: %v", err)
}
@@ -236,14 +236,14 @@ func TestFirstCommitNotification(t *testing.T) {
func checkFirstCommitNotification(
ctx context.Context,
- t testing.TB,
+ tb testing.TB,
member *integration.Member,
leaderAppliedIndex uint64,
notifier <-chan struct{},
) error {
// wait until server applies all the changes of leader
for member.Server.AppliedIndex() < leaderAppliedIndex {
- t.Logf("member.Server.AppliedIndex():%v <= leaderAppliedIndex:%v", member.Server.AppliedIndex(), leaderAppliedIndex)
+ tb.Logf("member.Server.AppliedIndex():%v <= leaderAppliedIndex:%v", member.Server.AppliedIndex(), leaderAppliedIndex)
select {
case <-ctx.Done():
return ctx.Err()
@@ -261,7 +261,7 @@ func checkFirstCommitNotification(
)
}
default:
- t.Logf("member.Server.AppliedIndex():%v >= leaderAppliedIndex:%v", member.Server.AppliedIndex(), leaderAppliedIndex)
+ tb.Logf("member.Server.AppliedIndex():%v >= leaderAppliedIndex:%v", member.Server.AppliedIndex(), leaderAppliedIndex)
return fmt.Errorf(
"notification was not triggered, member ID: %d",
member.ID(),
diff --git a/tests/integration/v3_lease_test.go b/tests/integration/v3_lease_test.go
index 532e94f3166e..3b8989248884 100644
--- a/tests/integration/v3_lease_test.go
+++ b/tests/integration/v3_lease_test.go
@@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"math"
+ "strconv"
"testing"
"time"
@@ -48,7 +49,7 @@ func TestV3LeasePromote(t *testing.T) {
defer clus.Terminate(t)
// create lease
- lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 3})
+ lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 3})
ttl := time.Duration(lresp.TTL) * time.Second
afterGrant := time.Now()
require.NoError(t, err)
@@ -103,7 +104,7 @@ func TestV3LeaseRevoke(t *testing.T) {
integration.BeforeTest(t)
testLeaseRemoveLeasedKey(t, func(clus *integration.Cluster, leaseID int64) error {
lc := integration.ToGRPC(clus.RandClient()).Lease
- _, err := lc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: leaseID})
+ _, err := lc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: leaseID})
return err
})
}
@@ -116,7 +117,7 @@ func TestV3LeaseGrantByID(t *testing.T) {
// create fixed lease
lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(
- context.TODO(),
+ t.Context(),
&pb.LeaseGrantRequest{ID: 1, TTL: 1})
if err != nil {
t.Errorf("could not create lease 1 (%v)", err)
@@ -127,7 +128,7 @@ func TestV3LeaseGrantByID(t *testing.T) {
// create duplicate fixed lease
_, err = integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(
- context.TODO(),
+ t.Context(),
&pb.LeaseGrantRequest{ID: 1, TTL: 1})
if !eqErrGRPC(err, rpctypes.ErrGRPCLeaseExist) {
t.Error(err)
@@ -135,7 +136,7 @@ func TestV3LeaseGrantByID(t *testing.T) {
// create fresh fixed lease
lresp, err = integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(
- context.TODO(),
+ t.Context(),
&pb.LeaseGrantRequest{ID: 2, TTL: 1})
if err != nil {
t.Errorf("could not create lease 2 (%v)", err)
@@ -179,7 +180,7 @@ func TestV3LeaseNegativeID(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
cc := clus.RandClient()
lresp, err := integration.ToGRPC(cc).Lease.LeaseGrant(ctx, &pb.LeaseGrantRequest{ID: tc.leaseID, TTL: 300})
@@ -233,7 +234,7 @@ func TestV3LeaseExpire(t *testing.T) {
testLeaseRemoveLeasedKey(t, func(clus *integration.Cluster, leaseID int64) error {
// let lease lapse; wait for deleted key
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
wStream, err := integration.ToGRPC(clus.RandClient()).Watch.Watch(ctx)
if err != nil {
@@ -265,7 +266,7 @@ func TestV3LeaseExpire(t *testing.T) {
errc <- err
case len(resp.Events) != 1:
fallthrough
- case resp.Events[0].Type != mvccpb.DELETE:
+ case resp.Events[0].Type != mvccpb.Event_DELETE:
errc <- fmt.Errorf("expected key delete, got %v", resp)
default:
errc <- nil
@@ -287,7 +288,7 @@ func TestV3LeaseKeepAlive(t *testing.T) {
testLeaseRemoveLeasedKey(t, func(clus *integration.Cluster, leaseID int64) error {
lc := integration.ToGRPC(clus.RandClient()).Lease
lreq := &pb.LeaseKeepAliveRequest{ID: leaseID}
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
lac, err := lc.LeaseKeepAlive(ctx)
if err != nil {
@@ -309,11 +310,197 @@ func TestV3LeaseKeepAlive(t *testing.T) {
}
time.Sleep(time.Duration(lresp.TTL/2) * time.Second)
}
- _, err = lc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: leaseID})
+ _, err = lc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: leaseID})
return err
})
}
+// TestV3LeaseKeepAliveForwardingCatchError ensures the server properly generates error
+// codes while the follower server is forwarding LeaseKeepAlive request to the leader.
+func TestV3LeaseKeepAliveForwardingCatchError(t *testing.T) {
+ integration.BeforeTest(t)
+ // Longer than leaseHandler.ServeHTTP()'s default timeout duration
+ sleepDuration := 8 * time.Second
+
+ t.Run("forwarding succeeds", func(t *testing.T) {
+ leader, follower, _ := setupLeaseForwardingCluster(t)
+ leaderClient := integration.ToGRPC(leader.Client).Lease
+
+ grantResp, err := leaderClient.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
+ require.NoError(t, err)
+ leaseID := grantResp.ID
+
+ keepAliveClient, err := integration.ToGRPC(follower.Client).Lease.LeaseKeepAlive(t.Context())
+ require.NoError(t, err)
+ defer keepAliveClient.CloseSend()
+
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ resp, err := keepAliveClient.Recv()
+ require.NoError(t, err)
+ require.Equal(t, leaseID, resp.ID)
+ require.Positive(t, resp.TTL)
+ })
+
+ t.Run("client cancels while forwarding", func(t *testing.T) {
+ integration.SkipIfNoGoFail(t)
+ leader, follower, _ := setupLeaseForwardingCluster(t)
+ leaderClient := integration.ToGRPC(leader.Client).Lease
+
+ grantResp, err := leaderClient.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
+ require.NoError(t, err)
+ leaseID := grantResp.ID
+
+ ctx, cancel := context.WithCancel(t.Context())
+ keepAliveClient, err := integration.ToGRPC(follower.Client).Lease.LeaseKeepAlive(ctx)
+ require.NoError(t, err)
+ defer keepAliveClient.CloseSend()
+
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ _, err = keepAliveClient.Recv()
+ require.NoError(t, err)
+
+ sleepBeforeServingLeaseRenew(t, sleepDuration)
+
+ // Use server metrics to verify behavior since client.Recv() always returns Canceled
+ // after cancel() regardless of the actual server response.
+ prevCanceledCount := getLeaseKeepAliveMetric(t, follower, "Canceled")
+ prevUnavailableCount := getLeaseKeepAliveMetric(t, follower, "Unavailable")
+
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ time.Sleep(50 * time.Millisecond)
+ cancel()
+
+ // Client sees Canceled (gRPC returns this immediately after cancel())
+ _, err = keepAliveClient.Recv()
+ require.Equal(t, codes.Canceled, status.Code(err))
+
+ require.Eventually(t, func() bool {
+ return getLeaseKeepAliveMetric(t, follower, "Canceled") == prevCanceledCount+1
+ }, 3*time.Second, 100*time.Millisecond)
+ require.Equal(t, prevUnavailableCount, getLeaseKeepAliveMetric(t, follower, "Unavailable"))
+ })
+
+ t.Run("forwarding times out", func(t *testing.T) {
+ integration.SkipIfNoGoFail(t)
+ leader, follower, _ := setupLeaseForwardingCluster(t)
+ leaderClient := integration.ToGRPC(leader.Client).Lease
+
+ grantResp, err := leaderClient.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
+ require.NoError(t, err)
+ leaseID := grantResp.ID
+
+ keepAliveClient, err := integration.ToGRPC(follower.Client).Lease.LeaseKeepAlive(t.Context())
+ require.NoError(t, err)
+ defer keepAliveClient.CloseSend()
+
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ _, err = keepAliveClient.Recv()
+ require.NoError(t, err)
+
+ sleepBeforeServingLeaseRenew(t, sleepDuration)
+
+ prevUnavailableCount := getLeaseKeepAliveMetric(t, follower, "Unavailable")
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+
+ _, err = keepAliveClient.Recv()
+ require.Equal(t, rpctypes.ErrGRPCTimeout, err)
+
+ require.Eventually(t, func() bool {
+ return getLeaseKeepAliveMetric(t, follower, "Unavailable") == prevUnavailableCount+1
+ }, 3*time.Second, 100*time.Millisecond)
+ })
+
+ // Client set up with WithRequireLeader() will receive NoLeader error right after
+ // monitorLeader() detects leader missing and cancels the server stream with ErrGRPCNoLeader.
+ t.Run("catches NoLeader error with WithRequireLeader", func(t *testing.T) {
+ leader, follower, anotherFollower := setupLeaseForwardingCluster(t)
+ followerClient := integration.ToGRPC(follower.Client).Lease
+
+ prevUnavailableCount := getLeaseKeepAliveMetric(t, follower, "Unavailable")
+ leader.Stop(t)
+ anotherFollower.Stop(t)
+
+ keepAliveClient, err := followerClient.LeaseKeepAlive(clientv3.WithRequireLeader(t.Context()))
+ require.NoError(t, err)
+
+ _, err = keepAliveClient.Recv()
+ require.Equal(t, rpctypes.ErrNoLeader.Error(), rpctypes.ErrorDesc(err))
+ // Skip metric check in proxy mode - metrics are recorded on the proxy, not the etcd server.
+ if !integration.ThroughProxy {
+ require.Equal(t, prevUnavailableCount+1, getLeaseKeepAliveMetric(t, follower, "Unavailable"))
+ }
+ })
+
+ // Client receives NoLeader error after the waitLeader() timed out in LeaseRenew().
+ t.Run("catches NoLeader error without WithRequireLeader", func(t *testing.T) {
+ leader, follower, anotherFollower := setupLeaseForwardingCluster(t)
+ leaderClient := integration.ToGRPC(leader.Client).Lease
+ followerClient := integration.ToGRPC(follower.Client).Lease
+
+ grantResp, err := leaderClient.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
+ require.NoError(t, err)
+ leaseID := grantResp.ID
+
+ keepAliveClient, err := followerClient.LeaseKeepAlive(t.Context())
+ require.NoError(t, err)
+ defer keepAliveClient.CloseSend()
+
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ _, err = keepAliveClient.Recv()
+ require.NoError(t, err)
+ prevUnavailableCount := getLeaseKeepAliveMetric(t, follower, "Unavailable")
+
+ leader.Stop(t)
+ anotherFollower.Stop(t)
+ require.NoError(t, keepAliveClient.Send(&pb.LeaseKeepAliveRequest{ID: leaseID}))
+ _, err = keepAliveClient.Recv()
+ if integration.ThroughProxy {
+ // Known limitation: grpcproxy doesn't propagate NoLeader error without
+ // WithRequireLeader. The keepAliveLoop in server/proxy/grpcproxy/lease.go
+ // discards errors and only calls cancel(), resulting in context.Canceled.
+ // TODO: Consider fixing grpcproxy to properly propagate errors.
+ require.ErrorIs(t, err, context.Canceled)
+ } else {
+ require.Equal(t, rpctypes.ErrNoLeader.Error(), rpctypes.ErrorDesc(err))
+ require.Equal(t, prevUnavailableCount+1, getLeaseKeepAliveMetric(t, follower, "Unavailable"))
+ }
+ })
+}
+
+func setupLeaseForwardingCluster(t *testing.T) (*integration.Member, *integration.Member, *integration.Member) {
+ t.Helper()
+ cluster := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
+ t.Cleanup(func() { cluster.Terminate(t) })
+
+ leaderIdx := cluster.WaitLeader(t)
+ return cluster.Members[leaderIdx], cluster.Members[(leaderIdx+1)%3], cluster.Members[(leaderIdx+2)%3]
+}
+
+func getLeaseKeepAliveMetric(t *testing.T, member *integration.Member, grpcCode string) int64 {
+ t.Helper()
+ metricVal, err := member.Metric(
+ "grpc_server_handled_total",
+ `grpc_method="LeaseKeepAlive"`,
+ fmt.Sprintf(`grpc_code="%v"`, grpcCode),
+ )
+ require.NoError(t, err)
+ count, err := strconv.ParseInt(metricVal, 10, 32)
+ require.NoError(t, err)
+ return count
+}
+
+func sleepBeforeServingLeaseRenew(t *testing.T, duration time.Duration) {
+ t.Helper()
+ failpointName := "beforeServeHTTPLeaseRenew"
+ require.NoError(t, gofail.Enable(failpointName, fmt.Sprintf(`sleep("%s")`, duration)))
+ t.Cleanup(func() {
+ terr := gofail.Disable(failpointName)
+ if terr != nil && !errors.Is(terr, gofail.ErrDisabled) {
+ t.Fatalf("Failed to disable failpoint %v, got error: %v", failpointName, terr)
+ }
+ })
+}
+
// TestV3LeaseCheckpoint ensures a lease checkpoint results in a remaining TTL being persisted
// across leader elections.
func TestV3LeaseCheckpoint(t *testing.T) {
@@ -375,7 +562,7 @@ func TestV3LeaseCheckpoint(t *testing.T) {
defer clus.Terminate(t)
// create lease
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
c := integration.ToGRPC(clus.RandClient())
lresp, err := c.Lease.LeaseGrant(ctx, &pb.LeaseGrantRequest{TTL: int64(tc.ttl.Seconds())})
@@ -428,7 +615,7 @@ func TestV3LeaseExists(t *testing.T) {
defer clus.Terminate(t)
// create lease
- ctx0, cancel0 := context.WithCancel(context.Background())
+ ctx0, cancel0 := context.WithCancel(t.Context())
defer cancel0()
lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(
ctx0,
@@ -447,7 +634,7 @@ func TestV3LeaseLeases(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx0, cancel0 := context.WithCancel(context.Background())
+ ctx0, cancel0 := context.WithCancel(t.Context())
defer cancel0()
// create leases
@@ -462,7 +649,7 @@ func TestV3LeaseLeases(t *testing.T) {
}
lresp, err := integration.ToGRPC(clus.RandClient()).Lease.LeaseLeases(
- context.Background(),
+ t.Context(),
&pb.LeaseLeasesRequest{})
require.NoError(t, err)
for i := range lresp.Leases {
@@ -505,7 +692,7 @@ func testLeaseStress(t *testing.T, stresser func(context.Context, pb.LeaseClient
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
errc := make(chan error)
@@ -583,7 +770,7 @@ func TestV3PutOnNonExistLease(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
badLeaseID := int64(0x12345678)
@@ -601,14 +788,14 @@ func TestV3GetNonExistLease(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
lc := integration.ToGRPC(clus.RandClient()).Lease
lresp, err := lc.LeaseGrant(ctx, &pb.LeaseGrantRequest{TTL: 10})
if err != nil {
t.Errorf("failed to create lease %v", err)
}
- _, err = lc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err = lc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
require.NoError(t, err)
leaseTTLr := &pb.LeaseTimeToLiveRequest{
@@ -639,7 +826,7 @@ func TestV3LeaseSwitch(t *testing.T) {
key := "foo"
// create lease
- ctx, cancel := context.WithCancel(context.Background())
+ ctx, cancel := context.WithCancel(t.Context())
defer cancel()
lresp1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(ctx, &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err1)
@@ -658,16 +845,16 @@ func TestV3LeaseSwitch(t *testing.T) {
_, err = integration.ToGRPC(clus.RandClient()).Lease.LeaseRevoke(ctx, &pb.LeaseRevokeRequest{ID: lresp1.ID})
require.NoError(t, err)
rreq := &pb.RangeRequest{Key: []byte("foo")}
- rresp, err := integration.ToGRPC(clus.RandClient()).KV.Range(context.TODO(), rreq)
+ rresp, err := integration.ToGRPC(clus.RandClient()).KV.Range(t.Context(), rreq)
require.NoError(t, err)
if len(rresp.Kvs) != 1 {
- t.Fatalf("unexpect removal of key")
+ t.Fatalf("unexpected removal of key")
}
// revoke lease2 should remove key
_, err = integration.ToGRPC(clus.RandClient()).Lease.LeaseRevoke(ctx, &pb.LeaseRevokeRequest{ID: lresp2.ID})
require.NoError(t, err)
- rresp, err = integration.ToGRPC(clus.RandClient()).KV.Range(context.TODO(), rreq)
+ rresp, err = integration.ToGRPC(clus.RandClient()).KV.Range(t.Context(), rreq)
require.NoError(t, err)
if len(rresp.Kvs) != 0 {
t.Fatalf("lease removed but key remains")
@@ -688,7 +875,7 @@ func TestV3LeaseFailover(t *testing.T) {
lc := integration.ToGRPC(clus.Client(toIsolate)).Lease
// create lease
- lresp, err := lc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 5})
+ lresp, err := lc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 5})
require.NoError(t, err)
require.Empty(t, lresp.Error)
@@ -698,7 +885,7 @@ func TestV3LeaseFailover(t *testing.T) {
lreq := &pb.LeaseKeepAliveRequest{ID: lresp.ID}
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
- mctx := metadata.NewOutgoingContext(context.Background(), md)
+ mctx := metadata.NewOutgoingContext(t.Context(), md)
ctx, cancel := context.WithCancel(mctx)
defer cancel()
lac, err := lc.LeaseKeepAlive(ctx)
@@ -730,43 +917,6 @@ func TestV3LeaseFailover(t *testing.T) {
}
}
-// TestV3LeaseRequireLeader ensures that a Recv will get a leader
-// loss error if there is no leader.
-func TestV3LeaseRequireLeader(t *testing.T) {
- integration.BeforeTest(t)
-
- clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
- defer clus.Terminate(t)
-
- lc := integration.ToGRPC(clus.Client(0)).Lease
- clus.Members[1].Stop(t)
- clus.Members[2].Stop(t)
-
- md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
- mctx := metadata.NewOutgoingContext(context.Background(), md)
- ctx, cancel := context.WithCancel(mctx)
- defer cancel()
- lac, err := lc.LeaseKeepAlive(ctx)
- require.NoError(t, err)
-
- donec := make(chan struct{})
- go func() {
- defer close(donec)
- resp, err := lac.Recv()
- if err == nil {
- t.Errorf("got response %+v, expected error", resp)
- }
- if rpctypes.ErrorDesc(err) != rpctypes.ErrNoLeader.Error() {
- t.Errorf("err = %v, want %v", err, rpctypes.ErrNoLeader)
- }
- }()
- select {
- case <-time.After(5 * time.Second):
- t.Fatal("did not receive leader loss error (in 5-sec)")
- case <-donec:
- }
-}
-
const fiveMinTTL int64 = 300
// TestV3LeaseRecoverAndRevoke ensures that revoking a lease after restart deletes the attached key.
@@ -779,10 +929,10 @@ func TestV3LeaseRecoverAndRevoke(t *testing.T) {
kvc := integration.ToGRPC(clus.Client(0)).KV
lsc := integration.ToGRPC(clus.Client(0)).Lease
- lresp, err := lsc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
+ lresp, err := lsc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
require.NoError(t, err)
require.Empty(t, lresp.Error)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
require.NoError(t, err)
// restart server and ensure lease still exists
@@ -799,9 +949,9 @@ func TestV3LeaseRecoverAndRevoke(t *testing.T) {
defer nc.Close()
// revoke should delete the key
- _, err = lsc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err = lsc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
require.NoError(t, err)
- rresp, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ rresp, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
if len(rresp.Kvs) != 0 {
t.Fatalf("lease removed but key remains")
@@ -818,14 +968,14 @@ func TestV3LeaseRevokeAndRecover(t *testing.T) {
kvc := integration.ToGRPC(clus.Client(0)).KV
lsc := integration.ToGRPC(clus.Client(0)).Lease
- lresp, err := lsc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
+ lresp, err := lsc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
require.NoError(t, err)
require.Empty(t, lresp.Error)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
require.NoError(t, err)
// revoke should delete the key
- _, err = lsc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err = lsc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
require.NoError(t, err)
// restart server and ensure revoked key doesn't exist
@@ -840,7 +990,7 @@ func TestV3LeaseRevokeAndRecover(t *testing.T) {
kvc = integration.ToGRPC(nc).KV
defer nc.Close()
- rresp, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ rresp, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
if len(rresp.Kvs) != 0 {
t.Fatalf("lease removed but key remains")
@@ -858,14 +1008,14 @@ func TestV3LeaseRecoverKeyWithDetachedLease(t *testing.T) {
kvc := integration.ToGRPC(clus.Client(0)).KV
lsc := integration.ToGRPC(clus.Client(0)).Lease
- lresp, err := lsc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
+ lresp, err := lsc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
require.NoError(t, err)
require.Empty(t, lresp.Error)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
require.NoError(t, err)
// overwrite lease with none
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar")})
require.NoError(t, err)
// restart server and ensure lease still exists
@@ -882,16 +1032,16 @@ func TestV3LeaseRecoverKeyWithDetachedLease(t *testing.T) {
defer nc.Close()
// revoke the detached lease
- _, err = lsc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: lresp.ID})
+ _, err = lsc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: lresp.ID})
require.NoError(t, err)
- rresp, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ rresp, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
if len(rresp.Kvs) != 1 {
t.Fatalf("only detached lease removed, key should remain")
}
}
-func TestV3LeaseRecoverKeyWithMutipleLease(t *testing.T) {
+func TestV3LeaseRecoverKeyWithMultipleLease(t *testing.T) {
integration.BeforeTest(t)
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1, UseBridge: true})
@@ -902,12 +1052,12 @@ func TestV3LeaseRecoverKeyWithMutipleLease(t *testing.T) {
var leaseIDs []int64
for i := 0; i < 2; i++ {
- lresp, err := lsc.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
+ lresp, err := lsc.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: fiveMinTTL})
require.NoError(t, err)
require.Empty(t, lresp.Error)
leaseIDs = append(leaseIDs, lresp.ID)
- _, err = kvc.Put(context.TODO(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
+ _, err = kvc.Put(t.Context(), &pb.PutRequest{Key: []byte("foo"), Value: []byte("bar"), Lease: lresp.ID})
require.NoError(t, err)
}
@@ -930,19 +1080,19 @@ func TestV3LeaseRecoverKeyWithMutipleLease(t *testing.T) {
defer nc.Close()
// revoke the old lease
- _, err = lsc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: leaseIDs[0]})
+ _, err = lsc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: leaseIDs[0]})
require.NoError(t, err)
// key should still exist
- rresp, err := kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ rresp, err := kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
if len(rresp.Kvs) != 1 {
t.Fatalf("only detached lease removed, key should remain")
}
// revoke the latest lease
- _, err = lsc.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: leaseIDs[1]})
+ _, err = lsc.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: leaseIDs[1]})
require.NoError(t, err)
- rresp, err = kvc.Range(context.TODO(), &pb.RangeRequest{Key: []byte("foo")})
+ rresp, err = kvc.Range(t.Context(), &pb.RangeRequest{Key: []byte("foo")})
require.NoError(t, err)
if len(rresp.Kvs) != 0 {
t.Fatalf("lease removed but key remains")
@@ -960,16 +1110,13 @@ func TestV3LeaseTimeToLiveWithLeaderChanged(t *testing.T) {
}
func testV3LeaseTimeToLiveWithLeaderChanged(t *testing.T, fpName string) {
- if len(gofail.List()) == 0 {
- t.Skip("please run 'make gofail-enable' before running the test")
- }
-
+ integration.SkipIfNoGoFail(t)
integration.BeforeTest(t)
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
defer cancel()
oldLeadIdx := clus.WaitLeader(t)
@@ -1055,7 +1202,7 @@ func testLeaseRemoveLeasedKey(t *testing.T, act func(*integration.Cluster, int64
// confirm no key
rreq := &pb.RangeRequest{Key: []byte("foo")}
- rresp, err := integration.ToGRPC(clus.RandClient()).KV.Range(context.TODO(), rreq)
+ rresp, err := integration.ToGRPC(clus.RandClient()).KV.Range(t.Context(), rreq)
require.NoError(t, err)
if len(rresp.Kvs) != 0 {
t.Fatalf("lease removed but key remains")
@@ -1065,9 +1212,9 @@ func testLeaseRemoveLeasedKey(t *testing.T, act func(*integration.Cluster, int64
func leaseExist(t *testing.T, clus *integration.Cluster, leaseID int64) bool {
l := integration.ToGRPC(clus.RandClient()).Lease
- _, err := l.LeaseGrant(context.Background(), &pb.LeaseGrantRequest{ID: leaseID, TTL: 5})
+ _, err := l.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{ID: leaseID, TTL: 5})
if err == nil {
- _, err = l.LeaseRevoke(context.Background(), &pb.LeaseRevokeRequest{ID: leaseID})
+ _, err = l.LeaseRevoke(t.Context(), &pb.LeaseRevokeRequest{ID: leaseID})
if err != nil {
t.Fatalf("failed to check lease %v", err)
}
diff --git a/tests/integration/v3_stm_test.go b/tests/integration/v3_stm_test.go
index a7c0781f0ded..a5df7fae08c5 100644
--- a/tests/integration/v3_stm_test.go
+++ b/tests/integration/v3_stm_test.go
@@ -40,7 +40,7 @@ func TestSTMConflict(t *testing.T) {
keys := make([]string, 5)
for i := 0; i < len(keys); i++ {
keys[i] = fmt.Sprintf("foo-%d", i)
- if _, err := etcdc.Put(context.TODO(), keys[i], "100"); err != nil {
+ if _, err := etcdc.Put(t.Context(), keys[i], "100"); err != nil {
t.Fatalf("could not make key (%v)", err)
}
}
@@ -90,7 +90,7 @@ func TestSTMConflict(t *testing.T) {
// ensure sum matches initial sum
sum := 0
for _, oldkey := range keys {
- rk, err := etcdc.Get(context.TODO(), oldkey)
+ rk, err := etcdc.Get(t.Context(), oldkey)
if err != nil {
t.Fatalf("couldn't fetch key %s (%v)", oldkey, err)
}
@@ -120,7 +120,7 @@ func TestSTMPutNewKey(t *testing.T) {
t.Fatalf("error on stm txn (%v)", err)
}
- resp, err := etcdc.Get(context.TODO(), "foo")
+ resp, err := etcdc.Get(t.Context(), "foo")
if err != nil {
t.Fatalf("error fetching key (%v)", err)
}
@@ -137,7 +137,7 @@ func TestSTMAbort(t *testing.T) {
defer clus.Terminate(t)
etcdc := clus.RandClient()
- ctx, cancel := context.WithCancel(context.TODO())
+ ctx, cancel := context.WithCancel(t.Context())
applyf := func(stm concurrency.STM) error {
stm.Put("foo", "baz")
cancel()
@@ -151,7 +151,7 @@ func TestSTMAbort(t *testing.T) {
t.Fatalf("no error on stm txn")
}
- resp, err := etcdc.Get(context.TODO(), "foo")
+ resp, err := etcdc.Get(t.Context(), "foo")
if err != nil {
t.Fatalf("error fetching key (%v)", err)
}
@@ -185,7 +185,7 @@ func TestSTMSerialize(t *testing.T) {
for _, k := range keys {
ops = append(ops, v3.OpPut(k, s))
}
- if _, err := etcdc.Txn(context.TODO()).Then(ops...).Commit(); err != nil {
+ if _, err := etcdc.Txn(t.Context()).Then(ops...).Commit(); err != nil {
t.Errorf("couldn't put keys (%v)", err)
}
updatec <- struct{}{}
@@ -231,12 +231,12 @@ func TestSTMApplyOnConcurrentDeletion(t *testing.T) {
defer clus.Terminate(t)
etcdc := clus.RandClient()
- _, err := etcdc.Put(context.TODO(), "foo", "bar")
+ _, err := etcdc.Put(t.Context(), "foo", "bar")
require.NoError(t, err)
donec, readyc := make(chan struct{}), make(chan struct{})
go func() {
<-readyc
- _, derr := etcdc.Delete(context.TODO(), "foo")
+ _, derr := etcdc.Delete(t.Context(), "foo")
assert.NoError(t, derr)
close(donec)
}()
@@ -262,7 +262,7 @@ func TestSTMApplyOnConcurrentDeletion(t *testing.T) {
t.Fatalf("STM apply expected to run twice, got %d", try)
}
- resp, err := etcdc.Get(context.TODO(), "foo2")
+ resp, err := etcdc.Get(t.Context(), "foo2")
if err != nil {
t.Fatalf("error fetching key (%v)", err)
}
@@ -279,7 +279,7 @@ func TestSTMSerializableSnapshotPut(t *testing.T) {
cli := clus.Client(0)
// key with lower create/mod revision than keys being updated
- _, err := cli.Put(context.TODO(), "a", "0")
+ _, err := cli.Put(t.Context(), "a", "0")
require.NoError(t, err)
tries := 0
@@ -299,7 +299,7 @@ func TestSTMSerializableSnapshotPut(t *testing.T) {
_, err = concurrency.NewSTM(cli, applyf, iso)
require.NoError(t, err)
- resp, err := cli.Get(context.TODO(), "b")
+ resp, err := cli.Get(t.Context(), "b")
require.NoError(t, err)
if resp.Kvs[0].Version != 2 {
t.Fatalf("bad version. got %+v, expected version 2", resp)
diff --git a/tests/integration/v3_tls_test.go b/tests/integration/v3_tls_test.go
index ea39edd46b61..b65867109e58 100644
--- a/tests/integration/v3_tls_test.go
+++ b/tests/integration/v3_tls_test.go
@@ -23,7 +23,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "google.golang.org/grpc"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/integration"
@@ -33,7 +32,7 @@ func TestTLSClientCipherSuitesValid(t *testing.T) { testTLSCipherSuites(t, tr
func TestTLSClientCipherSuitesMismatch(t *testing.T) { testTLSCipherSuites(t, false) }
// testTLSCipherSuites ensures mismatching client-side cipher suite
-// fail TLS handshake with the server.
+// fail the range request to the server.
func testTLSCipherSuites(t *testing.T, valid bool) {
integration.BeforeTest(t)
@@ -66,17 +65,22 @@ func testTLSCipherSuites(t *testing.T, valid bool) {
cli, cerr := integration.NewClient(t, clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
TLS: cc,
})
- if cli != nil {
- cli.Close()
- }
- if !valid && !errors.Is(cerr, context.DeadlineExceeded) {
- t.Fatalf("expected %v with TLS handshake failure, got %v", context.DeadlineExceeded, cerr)
- }
- if valid && cerr != nil {
- t.Fatalf("expected TLS handshake success, got %v", cerr)
+ require.NoError(t, cerr)
+ defer func() {
+ require.NoError(t, cli.Close())
+ }()
+
+ ctx, cancel := context.WithTimeout(t.Context(), 2*time.Second)
+ _, rerr := cli.Get(ctx, "foo")
+ cancel()
+ if valid {
+ require.NoError(t, rerr)
+ } else {
+ if !errors.Is(rerr, context.DeadlineExceeded) {
+ t.Fatalf("expected %v with TLS handshake failure, got %v", context.DeadlineExceeded, rerr)
+ }
}
}
@@ -129,12 +133,11 @@ func TestTLSMinMaxVersion(t *testing.T) {
cli, cerr := integration.NewClient(t, clientv3.Config{
Endpoints: []string{clus.Members[0].GRPCURL},
DialTimeout: time.Second,
- DialOptions: []grpc.DialOption{grpc.WithBlock()},
TLS: cc,
})
if cerr != nil {
assert.Truef(t, tt.expectError, "got TLS handshake error while expecting success: %v", cerr)
- assert.Equal(t, context.DeadlineExceeded, cerr)
+ assert.ErrorIs(t, cerr, context.DeadlineExceeded)
return
}
diff --git a/tests/integration/v3election_grpc_test.go b/tests/integration/v3election_grpc_test.go
index 25c7f846a9d7..94e27e71da53 100644
--- a/tests/integration/v3election_grpc_test.go
+++ b/tests/integration/v3election_grpc_test.go
@@ -15,7 +15,6 @@
package integration
import (
- "context"
"fmt"
"testing"
"time"
@@ -34,21 +33,21 @@ func TestV3ElectionCampaign(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err1)
- lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err2)
lc := integration.ToGRPC(clus.Client(0)).Election
req1 := &epb.CampaignRequest{Name: []byte("foo"), Lease: lease1.ID, Value: []byte("abc")}
- l1, lerr1 := lc.Campaign(context.TODO(), req1)
+ l1, lerr1 := lc.Campaign(t.Context(), req1)
require.NoError(t, lerr1)
campaignc := make(chan struct{})
go func() {
defer close(campaignc)
req2 := &epb.CampaignRequest{Name: []byte("foo"), Lease: lease2.ID, Value: []byte("def")}
- l2, lerr2 := lc.Campaign(context.TODO(), req2)
+ l2, lerr2 := lc.Campaign(t.Context(), req2)
if lerr2 != nil {
t.Error(lerr2)
}
@@ -63,7 +62,7 @@ func TestV3ElectionCampaign(t *testing.T) {
t.Fatalf("got leadership before resign")
}
- _, uerr := lc.Resign(context.TODO(), &epb.ResignRequest{Leader: l1.Leader})
+ _, uerr := lc.Resign(t.Context(), &epb.ResignRequest{Leader: l1.Leader})
require.NoError(t, uerr)
select {
@@ -72,7 +71,7 @@ func TestV3ElectionCampaign(t *testing.T) {
case <-campaignc:
}
- lval, lverr := lc.Leader(context.TODO(), &epb.LeaderRequest{Name: []byte("foo")})
+ lval, lverr := lc.Leader(t.Context(), &epb.LeaderRequest{Name: []byte("foo")})
require.NoError(t, lverr)
if string(lval.Kv.Value) != "def" {
@@ -93,7 +92,7 @@ func TestV3ElectionObserve(t *testing.T) {
observec := make(chan struct{}, 1)
go func() {
defer close(observec)
- s, err := lc.Observe(context.Background(), &epb.LeaderRequest{Name: []byte("foo")})
+ s, err := lc.Observe(t.Context(), &epb.LeaderRequest{Name: []byte("foo")})
observec <- struct{}{}
if err != nil {
t.Error(err)
@@ -119,9 +118,9 @@ func TestV3ElectionObserve(t *testing.T) {
t.Fatalf("observe stream took too long to start")
}
- lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err1)
- c1, cerr1 := lc.Campaign(context.TODO(), &epb.CampaignRequest{Name: []byte("foo"), Lease: lease1.ID, Value: []byte("0")})
+ c1, cerr1 := lc.Campaign(t.Context(), &epb.CampaignRequest{Name: []byte("foo"), Lease: lease1.ID, Value: []byte("0")})
require.NoError(t, cerr1)
// overlap other leader so it waits on resign
@@ -129,18 +128,18 @@ func TestV3ElectionObserve(t *testing.T) {
go func() {
defer close(leader2c)
- lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
if err2 != nil {
t.Error(err2)
}
- c2, cerr2 := lc.Campaign(context.TODO(), &epb.CampaignRequest{Name: []byte("foo"), Lease: lease2.ID, Value: []byte("5")})
+ c2, cerr2 := lc.Campaign(t.Context(), &epb.CampaignRequest{Name: []byte("foo"), Lease: lease2.ID, Value: []byte("5")})
if cerr2 != nil {
t.Error(cerr2)
}
for i := 6; i < 10; i++ {
v := []byte(fmt.Sprintf("%d", i))
req := &epb.ProclaimRequest{Leader: c2.Leader, Value: v}
- if _, err := lc.Proclaim(context.TODO(), req); err != nil {
+ if _, err := lc.Proclaim(t.Context(), req); err != nil {
t.Error(err)
}
}
@@ -149,11 +148,11 @@ func TestV3ElectionObserve(t *testing.T) {
for i := 1; i < 5; i++ {
v := []byte(fmt.Sprintf("%d", i))
req := &epb.ProclaimRequest{Leader: c1.Leader, Value: v}
- _, err := lc.Proclaim(context.TODO(), req)
+ _, err := lc.Proclaim(t.Context(), req)
require.NoError(t, err)
}
// start second leader
- lc.Resign(context.TODO(), &epb.ResignRequest{Leader: c1.Leader})
+ lc.Resign(t.Context(), &epb.ResignRequest{Leader: c1.Leader})
select {
case <-observec:
diff --git a/tests/integration/v3lock_grpc_test.go b/tests/integration/v3lock_grpc_test.go
index f79e455f22b2..575c86567d98 100644
--- a/tests/integration/v3lock_grpc_test.go
+++ b/tests/integration/v3lock_grpc_test.go
@@ -15,7 +15,6 @@
package integration
import (
- "context"
"testing"
"time"
@@ -33,18 +32,18 @@ func TestV3LockLockWaiter(t *testing.T) {
clus := integration.NewCluster(t, &integration.ClusterConfig{Size: 1})
defer clus.Terminate(t)
- lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease1, err1 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err1)
- lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(context.TODO(), &pb.LeaseGrantRequest{TTL: 30})
+ lease2, err2 := integration.ToGRPC(clus.RandClient()).Lease.LeaseGrant(t.Context(), &pb.LeaseGrantRequest{TTL: 30})
require.NoError(t, err2)
lc := integration.ToGRPC(clus.Client(0)).Lock
- l1, lerr1 := lc.Lock(context.TODO(), &lockpb.LockRequest{Name: []byte("foo"), Lease: lease1.ID})
+ l1, lerr1 := lc.Lock(t.Context(), &lockpb.LockRequest{Name: []byte("foo"), Lease: lease1.ID})
require.NoError(t, lerr1)
lockc := make(chan struct{})
go func() {
- l2, lerr2 := lc.Lock(context.TODO(), &lockpb.LockRequest{Name: []byte("foo"), Lease: lease2.ID})
+ l2, lerr2 := lc.Lock(t.Context(), &lockpb.LockRequest{Name: []byte("foo"), Lease: lease2.ID})
if lerr2 != nil {
t.Error(lerr2)
}
@@ -60,7 +59,7 @@ func TestV3LockLockWaiter(t *testing.T) {
t.Fatalf("locked before unlock")
}
- _, uerr := lc.Unlock(context.TODO(), &lockpb.UnlockRequest{Key: l1.Key})
+ _, uerr := lc.Unlock(t.Context(), &lockpb.UnlockRequest{Key: l1.Key})
require.NoError(t, uerr)
select {
diff --git a/tests/robustness/makefile.mk b/tests/robustness/Makefile
similarity index 67%
rename from tests/robustness/makefile.mk
rename to tests/robustness/Makefile
index 93a516ae9844..71e904246fa0 100644
--- a/tests/robustness/makefile.mk
+++ b/tests/robustness/Makefile
@@ -1,89 +1,118 @@
+REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
+
.PHONY: test-robustness-reports
-test-robustness-reports: export GOTOOLCHAIN := go$(shell cat .go-version)
+test-robustness-reports: export GOTOOLCHAIN := go$(shell cat $(REPOSITORY_ROOT)/.go-version)
test-robustness-reports:
- cd ./tests && go test ./robustness/validate -v --count 1 --run TestDataReports
+ cd $(REPOSITORY_ROOT)/tests && go test ./robustness/validate -v --count 1 --run TestDataReports
# Test main and previous release branches
+# Note that executing make at the top-level repository needs a change in the
+# directory. So, instead of calling just $(MAKE) or make, use this
+# $(TOPLEVEL_MAKE) variable.
+TOPLEVEL_MAKE := $(MAKE) --directory=$(REPOSITORY_ROOT)
+
.PHONY: test-robustness-main
-test-robustness-main: /tmp/etcd-main-failpoints/bin /tmp/etcd-release-3.5-failpoints/bin
- GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-main-failpoints/bin --bin-last-release=/tmp/etcd-release-3.5-failpoints/bin/etcd" $(MAKE) test-robustness
+test-robustness-main: /tmp/etcd-main-failpoints/bin /tmp/etcd-release-3.6-failpoints/bin
+ GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-main-failpoints/bin --bin-last-release=/tmp/etcd-release-3.6-failpoints/bin/etcd" $(TOPLEVEL_MAKE) test-robustness
+
+.PHONY: test-robustness-release-3.6
+test-robustness-release-3.6: /tmp/etcd-release-3.6-failpoints/bin /tmp/etcd-release-3.5-failpoints/bin
+ GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-release-3.6-failpoints/bin --bin-last-release=/tmp/etcd-release-3.5-failpoints/bin/etcd" $(TOPLEVEL_MAKE) test-robustness
.PHONY: test-robustness-release-3.5
test-robustness-release-3.5: /tmp/etcd-release-3.5-failpoints/bin /tmp/etcd-release-3.4-failpoints/bin
- GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-release-3.5-failpoints/bin --bin-last-release=/tmp/etcd-release-3.4-failpoints/bin/etcd" $(MAKE) test-robustness
+ GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-release-3.5-failpoints/bin --bin-last-release=/tmp/etcd-release-3.4-failpoints/bin/etcd" $(TOPLEVEL_MAKE) test-robustness
.PHONY: test-robustness-release-3.4
test-robustness-release-3.4: /tmp/etcd-release-3.4-failpoints/bin
- GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-release-3.4-failpoints/bin" $(MAKE) test-robustness
+ GO_TEST_FLAGS="$${GO_TEST_FLAGS} --bin-dir=/tmp/etcd-release-3.4-failpoints/bin" $(TOPLEVEL_MAKE) test-robustness
# Reproduce historical issues
.PHONY: test-robustness-issue14370
test-robustness-issue14370: /tmp/etcd-v3.5.4-failpoints/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue14370 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.4-failpoints/bin' $(MAKE) test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue14370 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.4-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue13766
test-robustness-issue13766: /tmp/etcd-v3.5.2-failpoints/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue13766 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.2-failpoints/bin' $(MAKE) test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue13766 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.2-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue14685
test-robustness-issue14685: /tmp/etcd-v3.5.5-failpoints/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue14685 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.5-failpoints/bin' $(MAKE) test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue14685 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.5-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
+ echo "Failed to reproduce" || echo "Successful reproduction"
+
+.PHONY: test-robustness-issue15220
+test-robustness-issue15220: /tmp/etcd-v3.5.7-failpoints/bin
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue15220 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.7-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue15271
test-robustness-issue15271: /tmp/etcd-v3.5.7-failpoints/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue15271 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.7-failpoints/bin' $(MAKE) test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue15271 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.7-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue17529
test-robustness-issue17529: /tmp/etcd-v3.5.12-beforeSendWatchResponse/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue17529 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.12-beforeSendWatchResponse/bin' $(MAKE) test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue17529 --count 100 --failfast --bin-dir=/tmp/etcd-v3.5.12-beforeSendWatchResponse/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue17780
test-robustness-issue17780: /tmp/etcd-v3.5.13-compactBeforeSetFinishedCompact/bin
- GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue17780 --count 200 --failfast --bin-dir=/tmp/etcd-v3.5.13-compactBeforeSetFinishedCompact/bin' make test-robustness && \
+ GO_TEST_FLAGS='-v --run=TestRobustnessRegression/Issue17780 --count 200 --failfast --bin-dir=/tmp/etcd-v3.5.13-compactBeforeSetFinishedCompact/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue18089
test-robustness-issue18089: /tmp/etcd-v3.5.12-beforeSendWatchResponse/bin
- GO_TEST_FLAGS='-v -run=TestRobustnessRegression/Issue18089 -count 100 -failfast --bin-dir=/tmp/etcd-v3.5.12-beforeSendWatchResponse/bin' make test-robustness && \
+ GO_TEST_FLAGS='-v -run=TestRobustnessRegression/Issue18089 -count 100 -failfast --bin-dir=/tmp/etcd-v3.5.12-beforeSendWatchResponse/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
.PHONY: test-robustness-issue19179
test-robustness-issue19179: /tmp/etcd-v3.5.17-failpoints/bin
- GO_TEST_FLAGS='-v -run=TestRobustnessRegression/Issue19179 -count 200 -failfast --bin-dir=/tmp/etcd-v3.5.17-failpoints/bin' make test-robustness && \
+ GO_TEST_FLAGS='-v -run=TestRobustnessRegression/Issue19179 -count 200 -failfast --bin-dir=/tmp/etcd-v3.5.17-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
echo "Failed to reproduce" || echo "Successful reproduction"
+.PHONY: test-robustness-issue20221
+test-robustness-issue20221: /tmp/etcd-bc47e771-failpoints/bin
+ GO_TEST_FLAGS='-v -run=TestRobustnessRegression/Issue20221 -count 100 -failfast --bin-dir=/tmp/etcd-bc47e771-failpoints/bin' $(TOPLEVEL_MAKE) test-robustness && \
+ echo "Failed to reproduce" || echo "Successful reproduction"
+
+# Etcd API usage by Kubernetes
+
+.PHONY: k8s-coverage
+k8s-coverage:
+ $(TOPLEVEL_MAKE) build
+ @echo "Running k8s coverage script"
+ ETCD_REPO="$(REPOSITORY_ROOT)" "$(REPOSITORY_ROOT)/tests/robustness/coverage/collect_kind_traces.sh"
+
# Failpoints
GOPATH = $(shell go env GOPATH)
-GOFAIL_VERSION = $(shell cd tools/mod && go list -m -f {{.Version}} go.etcd.io/gofail)
+GOFAIL_VERSION = $(shell cd $(REPOSITORY_ROOT)/tools/mod && go list -m -f {{.Version}} go.etcd.io/gofail)
.PHONY:install-gofail
install-gofail: $(GOPATH)/bin/gofail
.PHONY: gofail-enable
gofail-enable: $(GOPATH)/bin/gofail
- $(GOPATH)/bin/gofail enable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/
- cd ./server && go get go.etcd.io/gofail@${GOFAIL_VERSION}
- cd ./etcdutl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
- cd ./etcdctl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
- cd ./tests && go get go.etcd.io/gofail@${GOFAIL_VERSION}
+ $(GOPATH)/bin/gofail enable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
+ cd $(REPOSITORY_ROOT)/server && go get go.etcd.io/gofail@${GOFAIL_VERSION}
+ cd $(REPOSITORY_ROOT)/etcdutl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
+ cd $(REPOSITORY_ROOT)/etcdctl && go get go.etcd.io/gofail@${GOFAIL_VERSION}
+ cd $(REPOSITORY_ROOT)/tests && go get go.etcd.io/gofail@${GOFAIL_VERSION}
.PHONY: gofail-disable
gofail-disable: $(GOPATH)/bin/gofail
- $(GOPATH)/bin/gofail disable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/
- cd ./server && go mod tidy
- cd ./etcdutl && go mod tidy
- cd ./etcdctl && go mod tidy
- cd ./tests && go mod tidy
+ $(GOPATH)/bin/gofail disable server/etcdserver/ server/lease server/lease/leasehttp server/storage/backend/ server/storage/mvcc/ server/storage/wal/ server/etcdserver/api/v3rpc/ server/etcdserver/api/membership/ server/etcdserver/api/rafthttp/
+ cd $(REPOSITORY_ROOT)/server && go mod tidy
+ cd $(REPOSITORY_ROOT)/etcdutl && go mod tidy
+ cd $(REPOSITORY_ROOT)/etcdctl && go mod tidy
+ cd $(REPOSITORY_ROOT)/tests && go mod tidy
-$(GOPATH)/bin/gofail: tools/mod/go.mod tools/mod/go.sum
+$(GOPATH)/bin/gofail: $(REPOSITORY_ROOT)/tools/mod/go.mod $(REPOSITORY_ROOT)/tools/mod/go.sum
go install go.etcd.io/gofail@${GOFAIL_VERSION}
# Build main and previous releases for robustness tests
@@ -104,6 +133,25 @@ $(GOPATH)/bin/gofail: tools/mod/go.mod tools/mod/go.sum
$(MAKE) gofail-enable; \
$(MAKE) build;
+/tmp/etcd-bc47e771-failpoints/bin: $(GOPATH)/bin/gofail
+ rm -rf /tmp/etcd-bc47e771-failpoints/
+ mkdir -p /tmp/etcd-bc47e771-failpoints/
+ cd /tmp/etcd-bc47e771-failpoints/; \
+ git init; \
+ git remote add origin https://github.com/etcd-io/etcd.git; \
+ git fetch --depth 1 origin bc47e7711664ec5557c5ae528d0d02175ea6e166; \
+ git checkout FETCH_HEAD; \
+ $(MAKE) gofail-enable; \
+ $(MAKE) build;
+
+/tmp/etcd-release-3.6-failpoints/bin: $(GOPATH)/bin/gofail
+ rm -rf /tmp/etcd-release-3.6-failpoints/
+ mkdir -p /tmp/etcd-release-3.6-failpoints/
+ cd /tmp/etcd-release-3.6-failpoints/; \
+ git clone --depth 1 --branch release-3.6 https://github.com/etcd-io/etcd.git .; \
+ $(MAKE) gofail-enable; \
+ $(MAKE) build;
+
/tmp/etcd-v3.5.2-failpoints/bin:
/tmp/etcd-v3.5.4-failpoints/bin:
/tmp/etcd-v3.5.5-failpoints/bin:
diff --git a/tests/robustness/README.md b/tests/robustness/README.md
index e47e61d158d6..a5bed9dd276d 100644
--- a/tests/robustness/README.md
+++ b/tests/robustness/README.md
@@ -6,23 +6,40 @@ The purpose of these tests is to rigorously validate that etcd maintains its [KV
[KV API guarantees]: https://etcd.io/docs/v3.6/learning/api_guarantees/#kv-apis
[watch API guarantees]: https://etcd.io/docs/v3.6/learning/api_guarantees/#watch-apis
-## Robustness track record
+## Robustness vs Antithesis tests
+
+[Antithesis] runs the robustness tests inside their
+[deterministic simulation testing](https://antithesis.com/resources/deterministic_simulation_testing/)
+environment and [fault injection](https://antithesis.com/docs/environment/fault_injection/).
+
+For more details on Antithesis integration, see the [antithesis directory](../antithesis).
+
+[Antithesis]: https://antithesis.com/
-| Correctness / Consistency issue | Report | Introduced in | Discovered by | Reproducible by robustness test | Command |
-| ----------------------------------------------------------------- | ---------- | ----------------- | --------------- | ------------------------------------------------- | ----------------------------------- |
-| Inconsistent revision caused by crash during high load [#13766] | Mar 2022 | v3.5 | User | Yes, report preceded robustness tests | `make test-robustness-issue13766` |
-| Single node cluster can loose a write on crash [#14370] | Aug 2022 | v3.4 or earlier | User | Yes, report preceded robustness tests | `make test-robustness-issue14370` |
-| Enabling auth can lead to inconsistency [#14571] | Oct 2022 | v3.4 or earlier | User | No, authorization is not covered. | |
-| Inconsistent revision caused by crash during defrag [#14685] | Nov 2022 | v3.5 | Robustness | Yes, after covering defragmentation. | `make test-robustness-issue14685` |
-| Watch progress notification not synced with steam [#15220] | Jan 2023 | v3.4 or earlier | User | Yes, after covering watch progress notification | |
-| Watch traveling back in time after network partition [#15271] | Feb 2023 | v3.4 or earlier | Robustness | Yes, after covering network partitions | `make test-robustness-issue15271` |
-| Duplicated watch event due to bug in TXN caching [#17247] | Jan 2024 | main branch | Robustness | Yes, prevented regression in v3.6 | |
-| Watch events lost during stream starvation [#17529] | Mar 2024 | v3.4 or earlier | User | Yes, after covering of slow watch | `make test-robustness-issue17529` |
-| Revision decreasing caused by crash during compaction [#17780] | Apr 2024 | v3.4 or earlier | Robustness | Yes, after covering compaction | |
-| Watch dropping an event when compacting on delete [#18089] | May 2024 | v3.4 or earlier | Robustness | Yes, after covering of compaction | `make test-robustness-issue18089` |
-| Inconsistency when reading compacted revision in TXN [#18667] | Oct 2024 | v3.4 or earlier | User | | |
-| Missing delete event on watch opened on same revision as compaction [#19179] | Jan 2025 | v3.4 or earlier | Robustness | Yes, after covering of compaction | `make test-robustness-issue19179` |
+## Robustness track record
+| Correctness / Consistency issue | Report | Introduced in | Discovered by | Last reproduction commit | Reproduction Script |
+| ----------------------------------------------------------------- | -------- | ------------------ | --------------------------------------------------------- | -----------------------------| --------------------------------- |
+| Inconsistent revision caused by crash during high load [#13766] | Mar 2022 | v3.5 | User | Load not high enough | `make test-robustness-issue13766` |
+| Single node cluster can lose a write on crash [#14370] | Aug 2022 | v3.4 or earlier | User | [a438759] from Jan 3, 2026 | `make test-robustness-issue14370` |
+| Enabling auth can lead to inconsistency [#14571] | Oct 2022 | v3.4 or earlier | User | Authorization is not covered | |
+| Inconsistent revision caused by crash during defrag [#14685] | Nov 2022 | v3.5 | Robustness, after covering defragmentation | [a438759] from Jan 3, 2026 | `make test-robustness-issue14685` |
+| Watch progress notification not synced with stream [#15220] | Jan 2023 | v3.4 or earlier | User | [a438759] from Jan 3, 2026 | `make test-robustness-issue15220` |
+| Watch traveling back in time after network partition [#15271] | Feb 2023 | v3.4 or earlier | Robustness, after covering network partitions | | `make test-robustness-issue15271` |
+| Duplicated watch event due to bug in TXN caching [#17247] | Jan 2024 | main branch | Robustness, prevented regression on main branch | | |
+| Watch events lost during stream starvation [#17529] | Mar 2024 | v3.4 or earlier | User | [c272ade] from May 30, 2025 | `make test-robustness-issue17529` |
+| Revision decreasing caused by crash during compaction [#17780] | Apr 2024 | v3.4 or earlier | Robustness, after covering compaction | | |
+| Watch dropping an event when compacting on delete [#18089] | May 2024 | v3.4 or earlier | Robustness, after covering compaction | [a438759] from Jan 3, 2026 | `make test-robustness-issue18089` |
+| Panic when two snapshots are received in a short period [#18055] | May 2024 | v3.4 or earlier | Robustness | | |
+| Inconsistency when reading compacted revision in TXN [#18667] | Oct 2024 | v3.4 or earlier | User | | |
+| Missing delete event on watch opened on same revision as compaction [#19179] | Jan 2025 | v3.4 or earlier | Robustness, after covering compaction | | `make test-robustness-issue19179` |
+| Watch on future revision returns notifications [#20221] | Jun 2025 | v3.4 or earlier | Robustness, after covering connection to multiple members | | `make test-robustness-issue20221` |
+| Watch on future revision returns old events [#20221] | Jun 2025 | v3.4 or earlier | Antithesis, after covering connection to multiple members | | |
+| Panic from db page expected to be 5 [#20271] | Jul 2025 | v3.4 or earlier | Antithesis | | |
+| Stale reads caused by process pausing [#20418] | Jul 2025 | v3.5.0 and v3.4.20 | Antithesis | | |
+
+[c272ade]: https://github.com/etcd-io/etcd/tree/c272adec29afaa69f08b7458422c53b8978c7af1
+[a438759]: https://github.com/etcd-io/etcd/tree/a438759bf0bcafce851fae1a84a8511452b6b704
[#13766]: https://github.com/etcd-io/etcd/issues/13766
[#14370]: https://github.com/etcd-io/etcd/issues/14370
[#14571]: https://github.com/etcd-io/etcd/issues/14571
@@ -35,11 +52,31 @@ The purpose of these tests is to rigorously validate that etcd maintains its [KV
[#18089]: https://github.com/etcd-io/etcd/issues/18089
[#18667]: https://github.com/etcd-io/etcd/issues/18667
[#19179]: https://github.com/etcd-io/etcd/issues/19179
+[#20221]: https://github.com/etcd-io/etcd/issues/20221
+[#18055]: https://github.com/etcd-io/etcd/issues/18055
+[#20271]: https://github.com/etcd-io/etcd/issues/20271
+[#20418]: https://github.com/etcd-io/etcd/issues/20418
+
+## Maintaining Bug Reproducibility During Non-Trivial Changes
+
+When performing large non-trivial changes to the robustness testing framework, it is critical to ensure that we do not lose the ability to reproduce previously discovered bugs. The track record table above documents known correctness issues, and many include specific reproduction commands (e.g., `make test-robustness-issue14370`).
+
+To prevent regressions, we must ensure that the latest version of the robustness framework remains capable of reproducing old bugs.
+We manually track this capability in the "Last reproduction commit" column.
+**Best Practices:**
+
+* **Establish Baseline:** Before starting a large non-trivial change, run all reproducible test cases listed in the track record table.
+* **Verify Reproducibility:** After completing the change, verify that all previously reproducible bugs can still be detected.
+* **Update Tracking:** Refresh the "Last reproduction commit" column with commit hash and it's creation date to confirm the new framework version works.
+* **Update Commands:** If the change affects test execution, update the reproduction commands accordingly.
+* **Gate Completion:** Consider the change incomplete until all regression tests continue to catch their target bugs.
+
+This ensures that improvements to the testing framework do not inadvertently reduce our ability to detect known failure modes.
## How Robustness Tests Work
-Robustness tests compare etcd cluster behavior against a simplified model of its expected behavior.
+Robustness tests compare the etcd cluster behavior against a simplified model of its expected behavior.
These tests cover various scenarios, including:
* **Different etcd cluster setups:** Cluster sizes, configurations, and deployment topologies.
@@ -52,79 +89,83 @@ These tests cover various scenarios, including:
2. **Traffic and Failures:** Client traffic is generated and sent to the cluster while failures are injected.
3. **History Collection:** All client operations and their results are recorded.
4. **Validation:** The collected history is validated against the etcd model and a set of validators to ensure consistency and correctness.
-5. **Report Generation:** If a failure is detected and a detailed report is generated to help diagnose the issue.
- This report includes information about the client operations, etcd data directories.
+5. **Report Generation:** If a failure is detected then a detailed report is generated to help diagnose the issue.
+ This report includes information about the client operations and etcd data directories.
## Key Concepts
### Distributed System Terminology
-* **Consensus:** A process where nodes in a distributed system agree on a single data value. Etcd uses the Raft algorithm to achieve consensus.
-* **Strict vs Eventual consistency:**
- * **Strict Consistency:** All components see the same data at the same time after an update.
- * **Eventual Consistency:** Components may temporarily see different data after an update but converge to the same view eventually.
-* **Consistency Models (https://jepsen.io/consistency)**
- * **Single-Object Consistency Models:**
- * **Sequential Consistency:** A strong single-object model. Operations appear to take place in some total order, consistent with the order of operations on each individual process.
- * **Linearizable Consistency:** The strongest single-object model. Operations appear to happen instantly and in order, consistent with real-time ordering.
- * **Transactional Consistency Models**
- * **Serializable Consistency:** A transactional model guaranteeing that transactions appear to occur in some total order. Operations within a transaction are atomic and do not interleave with other transactions. It's a multi-object property, applying to the entire system, not just individual objects.
- * **Strict Serializable Consistency:** The strongest transactional model. Combines the total order of serializability with the real-time ordering constraints of linearizability.
+* **Consensus:** A process where nodes in a distributed system agree on a single data value. Etcd uses the Raft algorithm to achieve consensus.
+* **Strict vs Eventual consistency:**
+ * **Strict Consistency:** All components see the same data at the same time after an update.
+ * **Eventual Consistency:** Components may temporarily see different data after an update but converge to the same view eventually.
+* **Consistency Models ()**
+ * **Single-Object Consistency Models:**
+ * **Sequential Consistency:** A strong single-object model. Operations appear to take place in some total order, consistent with the order of operations on each individual process.
+ * **Linearizable Consistency:** The strongest single-object model. Operations appear to happen instantly and in order, consistent with real-time ordering.
+ * **Transactional Consistency Models**
+ * **Serializable Consistency:** A transactional model guaranteeing that transactions appear to occur in some total order. Operations within a transaction are atomic and do not interleave with other transactions. It's a multi-object property, applying to the entire system, not just individual objects.
+ * **Strict Serializable Consistency:** The strongest transactional model. Combines the total order of serializability with the real-time ordering constraints of linearizability.
Etcd provides strict serializability for KV operations and eventual consistency for Watch.
-**Etcd Guarantees**
+#### Etcd Guarantees
-* **Key-value API operations** https://etcd.io/docs/latest/learning/api_guarantees/#kv-apis
-* **Watch API guarantees** https://etcd.io/docs/latest/learning/api_guarantees/#watch-apis
+* **Key-value API operations**
+* **Watch API guarantees**
### Kubernetes Integration
-* **[Implicit Kubernetes-ETCD Contract]:** Defines how Kubernetes uses etcd to store cluster state.
-* **ResourceVersion:** A string used by Kubernetes to track resource versions, corresponding to etcd revisions.
-* **Sharding resource types:** Kubernetes treats each resource type as a totally independent entity.
+* **[Implicit Kubernetes-ETCD Contract]:** Defines how Kubernetes uses etcd to store cluster state.
+* **ResourceVersion:** A string used by Kubernetes to track resource versions, corresponding to etcd revisions.
+* **Sharding resource types:** Kubernetes treats each resource type as a totally independent entity.
It allows sharding each resource type into a separate etcd cluster.
[Implicit Kubernetes-ETCD Contract]: https://docs.google.com/document/d/1NUZDiJeiIH5vo_FMaTWf0JtrQKCx0kpEaIIuPoj9P6A/edit?usp=sharing
-## Running locally
+## Running locally
1. Build etcd with failpoints
+
```bash
make gofail-enable
make build
make gofail-disable
```
+
2. Run the tests
```bash
make test-robustness
```
-
- Optionally you can pass environment variables:
- * `GO_TEST_FLAGS` - to pass additional arguments to `go test`.
+
+ Optionally, you can pass environment variables:
+ * `GO_TEST_FLAGS` - to pass additional arguments to `go test`.
It is recommended to run tests multiple times with failfast enabled. this can be done by setting `GO_TEST_FLAGS='--count=100 --failfast'`.
* `EXPECT_DEBUG=true` - to get logs from the cluster.
- * `RESULTS_DIR` - to change location where results report will be saved.
+ * `RESULTS_DIR` - to change the location where the results report will be saved.
* `PERSIST_RESULTS` - to persist the results report of the test. By default this will not be persisted in the case of a successful run.
+ * `TRACING_SERVER_ADDR` - to export Open Telemetry traces from test runs to the collector running at given address, for example: `localhost:4317`
## Re-evaluate existing report
Robustness test validation is constantly changing and improving.
-Errors in etcd model could be causing false positives, which makes the ability to re-evaluate the reports after we fix the issue important.
+Errors in the etcd model could be causing false positives, which makes the ability to re-evaluate the reports after we fix the issue important.
> Note: Robustness test report format is not stable, and it's expected that not all old reports can be re-evaluated using the newest version.
-1. Identify location of the robustness test report.
+1. Identify the location of the robustness test report.
> Note: By default robustness test report is only generated for failed test.
* **For local runs:** this would be by identifying log line, in the following example that would be `/tmp/TestRobustnessExploratory_Etcd_HighTraffic_ClusterOfSize1`:
- ```
+
+ ```text
logger.go:146: 2024-04-08T09:45:27.734+0200 INFO Saving robustness test report {"path": "/tmp/TestRobustnessExploratory_Etcd_HighTraffic_ClusterOfSize1"}
```
- * **For remote runs on CI:** you need to go to the [Prow Dashboard](https://prow.k8s.io/job-history/gs/kubernetes-jenkins/logs/ci-etcd-robustness-amd64), go to a build, download one of the Artifacts (`artifacts/results.zip`), and extract it locally.
+ * **For remote runs on CI:** you need to go to the [Prow Dashboard](https://testgrid.k8s.io/sig-etcd-robustness#Summary), go to a build, download one of the Artifacts (`artifacts/results.zip`), and extract it locally.

@@ -144,15 +185,16 @@ Errors in etcd model could be causing false positives, which makes the ability t
The `testdata` directory can contain multiple robustness test reports.
The name of the report directory doesn't matter, as long as it's unique to prevent clashing with reports already present in `testdata` directory.
- For example path for `history.html` file could look like `$REPO_ROOT/tests/robustness/testdata/v3.5_failure_24_April/history.html`.
+ For example, the path for `history.html` file could look like `$REPO_ROOT/tests/robustness/testdata/v3.5_failure_24_April/history.html`.
3. Run `make test-robustness-reports` to validate all reports in the `testdata` directory.
## Analysing failure
-If robustness tests fails we want to analyse the report to confirm if the issue is on etcd side. Location of the directory with the report
-is mentioned `Saving robustness test report` log. Logs from report generation should look like:
-```
+If robustness tests fail, we want to analyse the report to confirm if the issue is on etcd side. The location of the directory with the report
+is mentioned in the `Saving robustness test report` log. Logs from report generation should look like:
+
+```text
logger.go:146: 2024-05-08T10:42:54.429+0200 INFO Saving robustness test report {"path": "/tmp/TestRobustnessRegression_Issue14370/1715157774429416550"}
logger.go:146: 2024-05-08T10:42:54.429+0200 INFO Saving member data dir {"member": "TestRobustnessRegressionIssue14370-test-0", "path": "/tmp/TestRobustnessRegression_Issue14370/1715157774429416550/server-TestRobustnessRegressionIssue14370-test-0"}
logger.go:146: 2024-05-08T10:42:54.430+0200 INFO no watch operations for client, skip persisting {"client-id": 1}
@@ -178,54 +220,59 @@ is mentioned `Saving robustness test report` log. Logs from report generation sh
logger.go:146: 2024-05-08T10:42:54.441+0200 INFO Saving visualization {"path": "/tmp/TestRobustnessRegression_Issue14370/1715157774429416550/history.html"}
```
-Report follows the hierarchy:
+The report follows the hierarchy:
+
* `server-*` - etcd server data directories, can be used to verify disk/memory corruption.
* `member`
* `wal` - Write Ahead Log (WAL) directory, that can be analysed using `etcd-dump-logs` command line tool available in `tools` directory.
* `snap` - Snapshot directory, includes the bbolt database file `db`, that can be analysed using `etcd-dump-db` command line tool available in `tools` directory.
* `client-*` - Client request and response dumps in json format.
- * `watch.jon` - Watch requests and responses, can be used to validate [watch API guarantees].
+ * `watch.json` - Watch requests and responses, can be used to validate [watch API guarantees].
* `operations.json` - KV operation history
* `history.html` - Visualization of KV operation history, can be used to validate [KV API guarantees].
-### Example analysis of linearization issue
+### Example analysis of a linearization issue
Let's reproduce and analyse robustness test report for issue [#14370].
To reproduce the issue by yourself run `make test-robustness-issue14370`.
-After a couple of tries robustness tests should fail with a log `Linearization failed` and save report locally.
+After a couple of tries robustness tests should fail with a log `Linearization illegal` and save the report locally.
Example:
-```
- logger.go:146: 2024-05-08T10:42:53.379+0200 INFO Validating linearizable operations {"timeout": "5m0s"}
- logger.go:146: 2024-05-08T10:42:54.429+0200 ERROR Linearization failed {"duration": "1.050105973s"}
- validate.go:39: Failed linearization, skipping further validation
- logger.go:146: 2024-05-08T10:42:54.429+0200 INFO Saving robustness test report {"path": "/tmp/TestRobustnessRegression_Issue14370/1715157774429416550"}
+
+```text
+ logger.go:146: 2025-08-01T22:54:26.550+0900 INFO Validating linearizable operations {"timeout": "5m0s"}
+ logger.go:146: 2025-08-01T22:54:26.755+0900 ERROR Linearization illegal {"duration": "205.05225ms"}
+ logger.go:146: 2025-08-01T22:54:26.755+0900 INFO Skipping other validations as linearization failed
+ main_test.go:122: linearization: illegal
+ logger.go:146: 2025-08-01T22:54:26.756+0900 INFO Saving robustness test report {"path": "/tmp/TestRobustnessRegression_Issue14370/1754056466755991000"}
...
- logger.go:146: 2024-05-08T10:42:54.441+0200 INFO Saving visualization {"path": "/tmp/TestRobustnessRegression_Issue14370/1715157774429416550/history.html"}
+ logger.go:146: 2025-08-01T22:54:26.850+0900 INFO Saving visualization {"path": "/tmp/TestRobustnessRegression_Issue14370/1754056466755991000/history.html"}
+ logger.go:146: 2025-08-01T22:54:26.878+0900 INFO killing server... {"name": "TestRobustnessRegressionIssue14370-test-0"}
+ logger.go:146: 2025-08-01T22:54:26.878+0900 INFO stopping server... {"name": "TestRobustnessRegressionIssue14370-test-0"}
+ logger.go:146: 2025-08-01T22:54:26.886+0900 INFO stopped server. {"name": "TestRobustnessRegressionIssue14370-test-0"}
```
Linearization issues are easiest to analyse via history visualization.
-Open `/tmp/TestRobustnessRegression_Issue14370/1715157774429416550/history.html` file in your browser.
+Open `/tmp/TestRobustnessRegression_Issue14370/1754056466755991000/history.html` file in your browser.
Jump to the error in linearization by clicking `[ jump to first error ]` on the top of the page.
You should see a graph similar to the one on the image below.

-Last correct request (connected with grey line) is a `Put` request that succeeded and got revision `168`.
-All following requests are invalid (connected with red line) as they have revision `167`.
-Etcd guarantee that revision is non-decreasing, so this shows a bug in etcd as there is no way revision should decrease.
-This is consistent with the root cause of [#14370] as it was issue with process crash causing last write to be lost.
-
-[#14370]: https://github.com/etcd-io/etcd/issues/14370
+The last correct request (connected with the grey line) is a `Put` request that succeeded and got revision `168`.
+All following requests are invalid (connected with red line) as they have revision `167`.
+Etcd guarantees that revision is non-decreasing, so this shows a bug in etcd as there is no way revision should decrease.
+This is consistent with the root cause of [#14370] as it was an issue with the process crash causing the last write to be lost.
-### Example analysis of watch issue
+### Example analysis of a watch issue
Let's reproduce and analyse robustness test report for issue [#15271].
To reproduce the issue by yourself run `make test-robustness-issue15271`.
After a couple of tries robustness tests should fail with a logs `Broke watch guarantee` and save report locally.
Example:
-```
+
+```text
logger.go:146: 2024-05-08T10:50:11.301+0200 INFO Validating linearizable operations {"timeout": "5m0s"}
logger.go:146: 2024-05-08T10:50:15.754+0200 INFO Linearization success {"duration": "4.453346487s"}
logger.go:146: 2024-05-08T10:50:15.754+0200 INFO Validating watch
@@ -236,22 +283,23 @@ Example:
```
Watch issues are easiest to analyse by reading the recorded watch history.
-Watch history is recorded for each client separated in different subdirectory under `/tmp/TestRobustnessRegression_Issue15271/1715158215866033806`
-Open `watch.json` for client mentioned in log `Broke watch guarantee`.
+
+Watch history is recorded for each client separated in different subdirectory under `/tmp/TestRobustnessRegression_Issue15271/1715158215866033806`.
+
+Open `watch.json` for the client mentioned in the log `Broke watch guarantee`.
For client `4` that broke the watch guarantee open `/tmp/TestRobustnessRegression_Issue15271/1715158215866033806/client-4/watch.json`.
-Each line consists of json blob corresponding to single watch request sent by client.
-Look for events with `Revision` equal to revision mentioned in the first log with `Broke watch guarantee`, in this case look for `"Revision":3,`.
+Each line consists of json blob corresponding to a single watch request sent by the client.
+Look for events with `Revision` equal to revision mentioned in the first log with `Broke watch guarantee`, in this case, look for `"Revision":3,`.
You should see watch responses where the `Revision` decreases like ones below:
-```
+
+```text
{"Events":[{"Type":"put-operation","Key":"key5","Value":{"Value":"793","Hash":0},"Revision":799,"IsCreate":false,"PrevValue":null}],"IsProgressNotify":false,"Revision":799,"Time":3202907249,"Error":""}
{"Events":[{"Type":"put-operation","Key":"key4","Value":{"Value":"1","Hash":0},"Revision":3,"IsCreate":true,"PrevValue":null}, ...
```
-Up to the first response the `Revision` of events only increased up to a value of `799`.
+Up to the first response, the `Revision` of events only increased up to a value of `799`.
However, the following line includes an event with `Revision` equal `3`.
-If you follow the `revision` throughout the file you should notice that watch replayed revisions second time.
+If you follow the `revision` throughout the file you should notice that watch replayed revisions for a second time.
This is incorrect and breaks `Ordered` [watch API guarantees].
-This is consistent with the root cause of [#14370] where member reconnecting to cluster will resend revisions.
-
-[#15271]: https://github.com/etcd-io/etcd/issues/15271
+This is consistent with the root cause of [#15271] where the member reconnecting to cluster will resend revisions.
diff --git a/tests/robustness/client/client.go b/tests/robustness/client/client.go
index 21a51832d724..e4399f53d33e 100644
--- a/tests/robustness/client/client.go
+++ b/tests/robustness/client/client.go
@@ -16,6 +16,7 @@ package client
import (
"context"
+ "errors"
"fmt"
"sync"
"time"
@@ -29,18 +30,19 @@ import (
"go.etcd.io/etcd/tests/v3/robustness/report"
)
-// RecordingClient provides a semi etcd client (different interface than
+// RecordingClient provides a semi-etcd client (different interface than
// clientv3.Client) that records all the requests and responses made. Doesn't
-// allow for concurrent requests to confirm to model.AppendableHistory requirements.
+// allow for concurrent requests to conform to model.AppendableHistory requirements.
type RecordingClient struct {
ID int
- client clientv3.Client
+ client *clientv3.Client
// using baseTime time-measuring operation to get monotonic clock reading
// see https://github.com/golang/go/blob/master/src/time/time.go#L17
baseTime time.Time
watchMux sync.Mutex
watchOperations []model.WatchOperation
+ wg sync.WaitGroup
// mux ensures order of request appending.
kvMux sync.Mutex
kvOperations *model.AppendableHistory
@@ -65,14 +67,16 @@ func NewRecordingClient(endpoints []string, ids identity.Provider, baseTime time
}
return &RecordingClient{
ID: ids.NewClientID(),
- client: *cc,
+ client: cc,
kvOperations: model.NewAppendableHistory(ids),
baseTime: baseTime,
}, nil
}
func (c *RecordingClient) Close() error {
- return c.client.Close()
+ err := c.client.Close()
+ c.wg.Wait()
+ return err
}
func (c *RecordingClient) Report() report.ClientReport {
@@ -89,10 +93,10 @@ func (c *RecordingClient) Do(ctx context.Context, op clientv3.Op) (clientv3.OpRe
func (c *RecordingClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
op := clientv3.OpGet(key, opts...)
- return c.Range(ctx, key, string(op.RangeBytes()), op.Rev(), op.Limit())
+ return c.Range(ctx, key, string(op.RangeBytes()), op.Rev(), op.Limit(), op.IsKeysOnly())
}
-func (c *RecordingClient) Range(ctx context.Context, start, end string, revision, limit int64) (*clientv3.GetResponse, error) {
+func (c *RecordingClient) Range(ctx context.Context, start, end string, revision, limit int64, keyOnly bool) (*clientv3.GetResponse, error) {
ops := []clientv3.OpOption{}
if end != "" {
ops = append(ops, clientv3.WithRange(end))
@@ -103,12 +107,42 @@ func (c *RecordingClient) Range(ctx context.Context, start, end string, revision
if limit != 0 {
ops = append(ops, clientv3.WithLimit(limit))
}
+ if keyOnly {
+ ops = append(ops, clientv3.WithKeysOnly())
+ }
c.kvMux.Lock()
defer c.kvMux.Unlock()
callTime := time.Since(c.baseTime)
resp, err := c.client.Get(ctx, start, ops...)
returnTime := time.Since(c.baseTime)
- c.kvOperations.AppendRange(start, end, revision, limit, callTime, returnTime, resp, err)
+ c.kvOperations.AppendRange(start, end, revision, limit, keyOnly, callTime, returnTime, resp, err)
+ return resp, err
+}
+
+func (c *RecordingClient) RangeStream(ctx context.Context, start, end string, revision, limit int64, keyOnly bool) (*clientv3.GetResponse, error) {
+ ops := []clientv3.OpOption{}
+ if end != "" {
+ ops = append(ops, clientv3.WithRange(end))
+ }
+ if revision != 0 {
+ ops = append(ops, clientv3.WithRev(revision))
+ }
+ if limit != 0 {
+ ops = append(ops, clientv3.WithLimit(limit))
+ }
+ if keyOnly {
+ ops = append(ops, clientv3.WithKeysOnly())
+ }
+ c.kvMux.Lock()
+ defer c.kvMux.Unlock()
+ callTime := time.Since(c.baseTime)
+ stream, err := c.client.GetStream(ctx, start, ops...)
+ var resp *clientv3.GetResponse
+ if err == nil {
+ resp, err = clientv3.GetStreamToGetResponse(stream)
+ }
+ returnTime := time.Since(c.baseTime)
+ c.kvOperations.AppendRange(start, end, revision, limit, keyOnly, callTime, returnTime, resp, err)
return resp, err
}
@@ -215,6 +249,10 @@ func (c *RecordingClient) Defragment(ctx context.Context) (*clientv3.DefragmentR
return resp, err
}
+func (c *RecordingClient) GetStream(ctx context.Context, key string, opts ...clientv3.OpOption) (clientv3.GetStreamChan, error) {
+ panic("not implemented")
+}
+
func (c *RecordingClient) Compact(ctx context.Context, rev int64, _ ...clientv3.CompactOption) (*clientv3.CompactResponse, error) {
c.kvMux.Lock()
defer c.kvMux.Unlock()
@@ -305,22 +343,30 @@ func (c *RecordingClient) watch(ctx context.Context, request model.WatchRequest)
}
respCh := make(chan clientv3.WatchResponse)
+ responses := []model.WatchResponse{}
c.watchMux.Lock()
c.watchOperations = append(c.watchOperations, model.WatchOperation{
Request: request,
- Responses: []model.WatchResponse{},
+ Responses: responses,
})
index := len(c.watchOperations) - 1
c.watchMux.Unlock()
+ c.wg.Add(1)
go func() {
+ defer c.wg.Done()
defer close(respCh)
for r := range c.client.Watch(ctx, request.Key, ops...) {
- c.watchOperations[index].Responses = append(c.watchOperations[index].Responses, ToWatchResponse(r, c.baseTime))
+ responses = append(responses, ToWatchResponse(r, c.baseTime))
+ c.watchMux.Lock()
+ c.watchOperations[index].Responses = responses
+ c.watchMux.Unlock()
select {
case respCh <- r:
case <-ctx.Done():
return
+ case <-c.client.Ctx().Done():
+ return
}
}
}()
@@ -336,7 +382,7 @@ func ToWatchResponse(r clientv3.WatchResponse, baseTime time.Time) model.WatchRe
// see https://github.com/golang/go/blob/master/src/time/time.go#L17
resp := model.WatchResponse{Time: time.Since(baseTime)}
for _, event := range r.Events {
- resp.Events = append(resp.Events, toWatchEvent(*event))
+ resp.Events = append(resp.Events, toWatchEvent(event))
}
resp.IsProgressNotify = r.IsProgressNotify()
resp.Revision = r.Header.Revision
@@ -347,7 +393,7 @@ func ToWatchResponse(r clientv3.WatchResponse, baseTime time.Time) model.WatchRe
return resp
}
-func toWatchEvent(event clientv3.Event) (watch model.WatchEvent) {
+func toWatchEvent(event *clientv3.Event) (watch model.WatchEvent) {
watch.Revision = event.Kv.ModRevision
watch.Key = string(event.Kv.Key)
watch.Value = model.ToValueOrHash(string(event.Kv.Value))
@@ -362,12 +408,90 @@ func toWatchEvent(event clientv3.Event) (watch model.WatchEvent) {
watch.IsCreate = event.IsCreate()
switch event.Type {
- case mvccpb.PUT:
+ case mvccpb.Event_PUT:
watch.Type = model.PutOperation
- case mvccpb.DELETE:
+ case mvccpb.Event_DELETE:
watch.Type = model.DeleteOperation
default:
panic(fmt.Sprintf("Unexpected event type: %s", event.Type))
}
return watch
}
+
+type ClientSet struct {
+ idProvider identity.Provider
+ baseTime time.Time
+
+ mux sync.Mutex
+ closed bool
+ clients []*RecordingClient
+ reports []report.ClientReport
+}
+
+func NewSet(ids identity.Provider, baseTime time.Time) *ClientSet {
+ return &ClientSet{
+ idProvider: ids,
+ baseTime: baseTime,
+
+ clients: []*RecordingClient{},
+ }
+}
+
+func (cs *ClientSet) NewClient(endpoints []string) (*RecordingClient, error) {
+ cs.mux.Lock()
+ defer cs.mux.Unlock()
+ if cs.closed {
+ return nil, errors.New("the clientset is already closed")
+ }
+ cli, err := NewRecordingClient(endpoints, cs.idProvider, cs.baseTime)
+ if err != nil {
+ return nil, err
+ }
+ cs.clients = append(cs.clients, cli)
+ return cli, nil
+}
+
+func (cs *ClientSet) Reports() []report.ClientReport {
+ cs.mux.Lock()
+ defer cs.mux.Unlock()
+ if !cs.closed {
+ cs.close()
+ }
+ if cs.reports == nil {
+ reports := cs.generateReports()
+ cs.reports = reports
+ }
+ return cs.reports
+}
+
+func (cs *ClientSet) Close() {
+ cs.mux.Lock()
+ defer cs.mux.Unlock()
+ cs.close()
+}
+
+func (cs *ClientSet) close() {
+ if cs.closed {
+ return
+ }
+ for _, c := range cs.clients {
+ c.Close()
+ }
+ cs.closed = true
+}
+
+func (cs *ClientSet) generateReports() []report.ClientReport {
+ reports := make([]report.ClientReport, 0, len(cs.clients))
+ for _, c := range cs.clients {
+ reports = append(reports, c.Report())
+ }
+ return reports
+}
+
+func (cs *ClientSet) IdentityProvider() identity.Provider {
+ return cs.idProvider
+}
+
+func (cs *ClientSet) BaseTime() time.Time {
+ return cs.baseTime
+}
diff --git a/tests/robustness/client/client_test.go b/tests/robustness/client/client_test.go
new file mode 100644
index 000000000000..59db1cfd8032
--- /dev/null
+++ b/tests/robustness/client/client_test.go
@@ -0,0 +1,83 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 client
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "go.etcd.io/etcd/api/v3/etcdserverpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/robustness/identity"
+ "go.etcd.io/etcd/tests/v3/robustness/model"
+)
+
+type mockWatcher struct {
+ ch chan clientv3.WatchResponse
+}
+
+func (m *mockWatcher) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
+ return m.ch
+}
+
+func (m *mockWatcher) RequestProgress(ctx context.Context) error {
+ return nil
+}
+
+func (m *mockWatcher) Close() error {
+ return nil
+}
+
+func TestRecordingClientWatchRace(t *testing.T) {
+ baseTime := time.Now()
+ ids := identity.NewIDProvider()
+
+ watchChan := make(chan clientv3.WatchResponse, 10)
+ watcher := &mockWatcher{ch: watchChan}
+
+ cc := clientv3.NewCtxClient(context.Background())
+ cc.Watcher = watcher
+
+ c := &RecordingClient{
+ ID: 1,
+ client: cc,
+ kvOperations: model.NewAppendableHistory(ids),
+ baseTime: baseTime,
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ request := model.WatchRequest{Key: "foo"}
+ _ = c.watch(ctx, request)
+
+ go func() {
+ time.Sleep(2 * time.Millisecond)
+ watchChan <- clientv3.WatchResponse{
+ Header: &etcdserverpb.ResponseHeader{Revision: 2},
+ }
+ time.Sleep(2 * time.Millisecond)
+ cancel()
+ close(watchChan)
+ }()
+
+ c.Close()
+
+ report := c.Report()
+ for _, op := range report.Watch {
+ _ = op.Responses
+ }
+}
diff --git a/tests/robustness/client/kvhash.go b/tests/robustness/client/kvhash.go
new file mode 100644
index 000000000000..5d982e04e9c9
--- /dev/null
+++ b/tests/robustness/client/kvhash.go
@@ -0,0 +1,69 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 client
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "go.uber.org/zap"
+
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/framework/e2e"
+)
+
+func CheckEndOfTestHashKV(ctx context.Context, clus *e2e.EtcdProcessCluster) error {
+ c, err := clientv3.New(clientv3.Config{
+ Endpoints: clus.EndpointsGRPC(),
+ Logger: zap.NewNop(),
+ DialKeepAliveTime: 10 * time.Second,
+ DialKeepAliveTimeout: 100 * time.Millisecond,
+ })
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ hashKV, err := c.HashKV(ctx, clus.EndpointsGRPC()[0], 0)
+ if err != nil {
+ return err
+ }
+ rev := hashKV.Header.Revision
+
+ hashKVs := make([]*clientv3.HashKVResponse, 0)
+ hashKVs = append(hashKVs, hashKV)
+
+ for _, member := range clus.Procs {
+ hashKV, err := c.HashKV(ctx, member.EndpointsGRPC()[0], rev)
+ if err != nil {
+ return err
+ }
+ if hashKV.Header.Revision != rev {
+ return fmt.Errorf("latest revision at the end of the test between nodes should be the same. Want %v, get %v", rev, hashKV.Header.Revision)
+ }
+ hashKVs = append(hashKVs, hashKV)
+ }
+
+ for i := 1; i < len(hashKVs); i++ {
+ if hashKVs[i-1].CompactRevision != hashKVs[i].CompactRevision {
+ return fmt.Errorf("compactRevision mismatch, node %v has %+v, node %v has %+v", hashKVs[i-1].Header.MemberId, hashKVs[i-1], hashKVs[i].Header.MemberId, hashKVs[i])
+ }
+ if hashKVs[i-1].Hash != hashKVs[i].Hash {
+ return fmt.Errorf("hash mismatch, node %v has %+v, node %v has %+v", hashKVs[i-1].Header.MemberId, hashKVs[i-1], hashKVs[i].Header.MemberId, hashKVs[i])
+ }
+ }
+ return nil
+}
diff --git a/tests/robustness/client/watch.go b/tests/robustness/client/watch.go
index a9cdbbf0f940..84765520c3dd 100644
--- a/tests/robustness/client/watch.go
+++ b/tests/robustness/client/watch.go
@@ -16,92 +16,89 @@ package client
import (
"context"
- "sync"
- "testing"
+ "errors"
+ "fmt"
"time"
- "github.com/stretchr/testify/require"
-
- "go.etcd.io/etcd/tests/v3/framework/e2e"
- "go.etcd.io/etcd/tests/v3/robustness/identity"
- "go.etcd.io/etcd/tests/v3/robustness/report"
+ "go.uber.org/zap"
+ "golang.org/x/sync/errgroup"
)
-func CollectClusterWatchEvents(ctx context.Context, t *testing.T, clus *e2e.EtcdProcessCluster, maxRevisionChan <-chan int64, cfg WatchConfig, baseTime time.Time, ids identity.Provider) []report.ClientReport {
- mux := sync.Mutex{}
- var wg sync.WaitGroup
- reports := make([]report.ClientReport, len(clus.Procs))
- memberMaxRevisionChans := make([]chan int64, len(clus.Procs))
- for i, member := range clus.Procs {
- c, err := NewRecordingClient(member.EndpointsGRPC(), ids, baseTime)
- require.NoError(t, err)
+type CollectClusterWatchEventsParam struct {
+ Lg *zap.Logger
+ Endpoints []string
+ MaxRevisionChan <-chan int64
+ ClientSet *ClientSet
+}
+
+func CollectClusterWatchEvents(ctx context.Context, param CollectClusterWatchEventsParam) error {
+ var g errgroup.Group
+ memberMaxRevisionChans := make([]chan int64, len(param.Endpoints))
+ for i, endpoint := range param.Endpoints {
memberMaxRevisionChan := make(chan int64, 1)
memberMaxRevisionChans[i] = memberMaxRevisionChan
- wg.Add(1)
- go func(i int, c *RecordingClient) {
- defer wg.Done()
+ g.Go(func() error {
+ c, err := param.ClientSet.NewClient([]string{endpoint})
+ if err != nil {
+ return err
+ }
defer c.Close()
- watchUntilRevision(ctx, t, c, memberMaxRevisionChan, cfg)
- mux.Lock()
- reports[i] = c.Report()
- mux.Unlock()
- }(i, c)
+ return watchUntilRevision(ctx, param.Lg, c, memberMaxRevisionChan)
+ })
}
- wg.Add(1)
- go func() {
- defer wg.Done()
- maxRevision := <-maxRevisionChan
+ g.Go(func() error {
+ maxRevision := <-param.MaxRevisionChan
for _, memberChan := range memberMaxRevisionChans {
memberChan <- maxRevision
}
- }()
- wg.Wait()
- return reports
-}
+ return nil
+ })
-type WatchConfig struct {
- RequestProgress bool
+ return g.Wait()
}
-// watchUntilRevision watches all changes until context is cancelled, it has observed revision provided via maxRevisionChan or maxRevisionChan was closed.
-func watchUntilRevision(ctx context.Context, t *testing.T, c *RecordingClient, maxRevisionChan <-chan int64, cfg WatchConfig) {
+// watchUntilRevision watches all changes until context is canceled, it has observed the revision provided via maxRevisionChan or maxRevisionChan was closed.
+func watchUntilRevision(ctx context.Context, lg *zap.Logger, c *RecordingClient, maxRevisionChan <-chan int64) error {
var maxRevision int64
var lastRevision int64 = 1
+ var closing bool
ctx, cancel := context.WithCancel(ctx)
defer cancel()
resetWatch:
for {
+ if closing {
+ if maxRevision == 0 {
+ return errors.New("client didn't collect all events, max revision not set")
+ }
+ if lastRevision < maxRevision {
+ return fmt.Errorf("client didn't collect all events, got: %d, expected: %d", lastRevision, maxRevision)
+ }
+ return nil
+ }
watch := c.Watch(ctx, "", lastRevision+1, true, true, false)
for {
select {
- case <-ctx.Done():
- if maxRevision == 0 {
- t.Errorf("Client didn't collect all events, max revision not set")
- }
- if lastRevision < maxRevision {
- t.Errorf("Client didn't collect all events, revision got %d, expected: %d", lastRevision, maxRevision)
- }
- return
case revision, ok := <-maxRevisionChan:
if ok {
maxRevision = revision
if lastRevision >= maxRevision {
+ closing = true
cancel()
}
} else {
// Only cancel if maxRevision was never set.
if maxRevision == 0 {
+ closing = true
cancel()
}
}
+ case <-time.After(100 * time.Millisecond):
+ c.RequestProgress(ctx)
case resp, ok := <-watch:
if !ok {
- t.Logf("Watch channel closed")
+ lg.Info("Watch channel closed")
continue resetWatch
}
- if cfg.RequestProgress {
- c.RequestProgress(ctx)
- }
if resp.Err() != nil {
if resp.Canceled {
@@ -110,12 +107,13 @@ resetWatch:
}
continue resetWatch
}
- t.Errorf("Watch stream received error, err %v", resp.Err())
+ return fmt.Errorf("watch stream received error: %w", resp.Err())
}
if len(resp.Events) > 0 {
lastRevision = resp.Events[len(resp.Events)-1].Kv.ModRevision
}
if maxRevision != 0 && lastRevision >= maxRevision {
+ closing = true
cancel()
}
}
diff --git a/tests/robustness/coverage/README.md b/tests/robustness/coverage/README.md
new file mode 100644
index 000000000000..5d6c69f2d80c
--- /dev/null
+++ b/tests/robustness/coverage/README.md
@@ -0,0 +1,92 @@
+## Overview
+
+Go tests in this directory analyze the usage of Etcd API based on collected
+traces from a Kubernetes cluster. They output information on:
+
+1. Number of calls per gRPC method used by Kubernetes
+1. Key pattern
+1. Arguments provided to KV, Watch and Lease methods
+
+This information can be used to track the coverage of k8s-etcd contract.
+
+### Manual test execution
+
+At first we will manually set up the cluster, run e2e tests, download traces and
+then execute the test.
+
+1. Customize and set the environment variables used by the code snippets below:
+
+```shell
+# Used for patches, building kind nodes, and running e2e tests.
+export KUBERNETES_REPO="$(go env GOPATH)/src/k8s.io/kubernetes"
+# Used when creating kind cluster and running e2e tests.
+export KUBECONFIG="${KUBERNETES_REPO}/kind-with-tracing-config"
+```
+
+1. Set up [KIND
+cluster](https://kind.sigs.k8s.io/docs/user/quick-start/#installation), exercise
+Kubernetes API, export traces to [Jaeger](https://www.jaegertracing.io/) and
+then download them:
+
+ ```shell
+ make k8s-coverage
+ ```
+
+1. Run Go test
+
+ ```shell
+ go test -v -timeout 60s go.etcd.io/etcd/tests/v3/robustness/coverage
+ ```
+
+1. Clean up the environment
+
+ ```shell
+ kind delete cluster --name kind-with-external-etcd
+ docker network rm kind-with-external-etcd
+ ```
+
+### Manual trace collection from robustness tests
+
+1. Run [Jaeger](https://www.jaegertracing.io/) container:
+
+ ```shell
+ docker run --rm --name jaeger \
+ -p 16686:16686 \
+ -p 4317:4317 \
+ jaegertracing/jaeger:2.6.0 --set=extensions.jaeger_storage.backends.some_storage.memory.max_traces=20000000
+ ```
+
+1. Run robustness tests. For example:
+
+ ```shell
+ env \
+ TRACING_SERVER_ADDR=localhost:4317 \
+ GO_TEST_FLAGS='--timeout 10m --count=1 -v --run "^TestRobustness.*/Kubernetes.*"' \
+ make test-robustness
+ ```
+
+1. Download traces and put them into `tests/robustness/coverage/testdata`
+directory in Etcd git repository:
+
+ ```shell
+ curl -v --get --retry 10 --retry-connrefused -o testdata/demo_traces.json \
+ -H "Content-Type: application/json" \
+ --data-urlencode "query.start_time_min=$(date --date="5 days ago" -Ins)" \
+ --data-urlencode "query.start_time_max=$(date -Ins)" \
+ --data-urlencode "query.service_name=etcd" \
+ "http://localhost:16686/api/v3/traces"
+ ```
+
+1. Run Go test
+
+ ```shell
+ go test -v -timeout 60s go.etcd.io/etcd/tests/v3/robustness/coverage
+ ```
+
+ It will show the coverage of Kubernetes-Etcd surface by robustness tests.:w
+
+### Automated test execution
+
+Work on improving these tests is tracked in
+[#20182](https://github.com/etcd-io/etcd/issues/20182) and periodic runs in
+[#20642](https://github.com/etcd-io/etcd/issues/20642).
diff --git a/tests/robustness/coverage/apiserver-shared-conf/tracing.yaml b/tests/robustness/coverage/apiserver-shared-conf/tracing.yaml
new file mode 100644
index 000000000000..d0f87e0df336
--- /dev/null
+++ b/tests/robustness/coverage/apiserver-shared-conf/tracing.yaml
@@ -0,0 +1,5 @@
+---
+apiVersion: apiserver.config.k8s.io/v1beta1
+kind: TracingConfiguration
+endpoint: 192.168.32.1:4317
+samplingRatePerMillion: 1000000
diff --git a/tests/robustness/coverage/collect_kind_traces.sh b/tests/robustness/coverage/collect_kind_traces.sh
new file mode 100755
index 000000000000..0f9fa2c27030
--- /dev/null
+++ b/tests/robustness/coverage/collect_kind_traces.sh
@@ -0,0 +1,123 @@
+#!/usr/bin/env bash
+# Copyright 2025 The etcd Authors
+#
+# 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.
+
+# This script runs KIND (Kubernetes IN Docker) cluster to collect traces with
+# Jaeger for analysis in robustness/coverage tests.
+# It is based on instructions from tests/robustness/coverage/README.md
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+# 1. Customize and set the environment variables
+export KUBERNETES_REPO="${KUBERNETES_REPO:-$(go env GOPATH)/src/k8s.io/kubernetes}"
+export ETCD_REPO="${ETCD_REPO:-$(go env GOPATH)/src/go.etcd.io/etcd}"
+export KUBECONFIG="${KUBERNETES_REPO}/kind-with-tracing-config"
+
+echo "Using KUBERNETES_REPO: ${KUBERNETES_REPO}"
+echo "Using ETCD_REPO: ${KUBERNETES_REPO}"
+echo "Using KUBECONFIG: ${KUBECONFIG}"
+
+echo "Applying patches to Kubernetes repo..."
+PATCHES_DIR="${ETCD_REPO}/tests/robustness/coverage/patches/kubernetes"
+pushd "${KUBERNETES_REPO}"
+git apply --reverse --check "${PATCHES_DIR}/"* || git apply --recount "${PATCHES_DIR}/"*
+echo "Building KIND node image..."
+kind build node-image
+popd
+
+echo "Creating Docker network..."
+docker network create kind-with-external-etcd \
+ --driver bridge \
+ --gateway "192.168.32.1" \
+ --subnet "192.168.32.0/24" || echo "Docker network already exists."
+rm_docker_network() {
+ docker network rm kind-with-external-etcd
+}
+trap "rm_docker_network" EXIT SIGINT
+
+echo "Starting Jaeger container..."
+docker stop jaeger && docker wait jaeger || echo
+docker run --rm --detach --name jaeger \
+ -p 16686:16686 \
+ -p 4317:4317 \
+ jaegertracing/jaeger:2.6.0 --set=extensions.jaeger_storage.backends.some_storage.memory.max_traces=20000000
+stop_jaeger() {
+ docker stop jaeger
+ rm_docker_network
+}
+trap "stop_jaeger" EXIT SIGINT
+
+echo "Building and starting etcd..."
+pushd "${ETCD_REPO}"
+mkdir -p "${KUBERNETES_REPO}/third_party/etcd"
+DATA_DIR="$(mktemp -d -p "${DATA_DIR:-/tmp}")"
+export DATA_DIR
+cp "./bin/etcd" "${KUBERNETES_REPO}/third_party/etcd/etcd"
+"./bin/etcd" --watch-progress-notify-interval=5s \
+ --data-dir "${DATA_DIR}" \
+ --listen-client-urls "http://192.168.32.1:2379" \
+ --advertise-client-urls "http://192.168.32.1:2379" \
+ --enable-distributed-tracing \
+ --distributed-tracing-address="192.168.32.1:4317" \
+ --distributed-tracing-service-name="etcd" \
+ --distributed-tracing-sampling-rate=1000000 &
+ETCD_PID=$!
+echo "etcd started with PID: ${ETCD_PID}"
+popd
+stop_etcd() {
+ kill "${ETCD_PID}"
+ rm -rf "${DATA_DIR}"
+ stop_jaeger
+}
+trap "stop_etcd" EXIT SIGINT
+
+echo "Creating KIND cluster..."
+export KIND_EXPERIMENTAL_DOCKER_NETWORK=kind-with-external-etcd
+kind delete cluster --name kind-with-external-etcd
+delete_kind_cluster() {
+ kind delete cluster --name kind-with-external-etcd
+ stop_etcd
+}
+trap "delete_kind_cluster" EXIT SIGINT
+pushd "${ETCD_REPO}/tests/robustness/coverage"
+kind create cluster --config kind-with-tracing.yaml --name kind-with-external-etcd --image kindest/node:latest
+popd
+
+echo "Running Kubernetes e2e tests..."
+pushd "${KUBERNETES_REPO}"
+make WHAT="test/e2e/e2e.test"
+./_output/bin/e2e.test \
+ -context kind-kind-with-external-etcd \
+ -ginkgo.focus="\[sig-apps\].*Conformance" \
+ -num-nodes 2 || echo "[sig-apps] Conformance tests failed. Ignoring..."
+echo "Running Kubernetes cmd tests..."
+./build/run.sh ./hack/jenkins/test-cmd-dockerized.sh || echo "Command tests failed. Ignoring..."
+popd
+
+echo "Downloading traces..."
+curl -v --get --retry 10 --retry-connrefused -o "${ETCD_REPO}/tests/robustness/coverage/testdata/traces.json" \
+ -H "Content-Type: application/json" \
+ --data-urlencode "query.start_time_min=$(date --date="5 days ago" -Ins)" \
+ --data-urlencode "query.start_time_max=$(date --date="2 minutes ago" -Ins)" \
+ --data-urlencode "query.service_name=etcd" \
+ "http://192.168.32.1:16686/api/v3/traces"
+
+
+echo "Cleaning up..."
+set +o errexit
+delete_kind_cluster
+
+echo "Done."
diff --git a/tests/robustness/coverage/contract_test.go b/tests/robustness/coverage/contract_test.go
new file mode 100644
index 000000000000..46fd581467e3
--- /dev/null
+++ b/tests/robustness/coverage/contract_test.go
@@ -0,0 +1,119 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 coverage_test
+
+import (
+ "iter"
+ "strings"
+ "testing"
+
+ tracev1 "go.opentelemetry.io/proto/otlp/trace/v1"
+)
+
+func contract(spansByID map[string]*tracev1.Span, span *tracev1.Span) (string, bool) {
+ for span := range walk(spansByID, span) {
+ for _, event := range span.GetEvents() {
+ name := event.GetName()
+ if strings.HasPrefix(name, "contract.") {
+ return name, true
+ }
+ }
+ }
+ return "", false
+}
+
+// walk iterates over the chain of spans starting from span and then going up
+// the tree to parent span if such exists.
+func walk(spansByID map[string]*tracev1.Span, span *tracev1.Span) iter.Seq[*tracev1.Span] {
+ return func(yield func(*tracev1.Span) bool) {
+ for node := span; node != nil; node = spansByID[string(node.GetParentSpanId())] {
+ if !yield(node) {
+ return
+ }
+ }
+ }
+}
+
+func TestContract(t *testing.T) {
+ spansByID := map[string]*tracev1.Span{
+ "child": {
+ ParentSpanId: []byte("middle"),
+ },
+ "middle": {
+ ParentSpanId: []byte("parent"),
+ Events: []*tracev1.Span_Event{
+ {Name: "wrong.contract"},
+ },
+ },
+ "parent": {
+ ParentSpanId: []byte("grandparent"),
+ Events: []*tracev1.Span_Event{
+ {Name: "contract.OptimisticPut"},
+ },
+ },
+ "grandparent": {
+ Events: []*tracev1.Span_Event{
+ {Name: "contract.Get"},
+ },
+ },
+ "outside_delete": {
+ Events: []*tracev1.Span_Event{
+ {Name: "otherEvent"},
+ {Name: "contract.OptimisticDelete"},
+ },
+ },
+ "outside_invalid": {
+ Events: []*tracev1.Span_Event{
+ {Name: "contractWrong"},
+ },
+ },
+ }
+
+ for _, tc := range []struct {
+ name string
+ source string
+ want string
+ found bool
+ }{
+ {
+ name: "ok_chain",
+ source: "child",
+ want: "contract.OptimisticPut",
+ found: true,
+ },
+ {
+ name: "ok_single",
+ source: "outside_delete",
+ want: "contract.OptimisticDelete",
+ found: true,
+ },
+ {
+ name: "not_in_contract_chain",
+ source: "outside_invalid",
+ want: "",
+ found: false,
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ got, found := contract(spansByID, spansByID[tc.source])
+ if found != tc.found {
+ t.Errorf("got=%v, want=%v", found, tc.found)
+ }
+ if got != tc.want {
+ t.Errorf("got=%v, want=%v", got, tc.want)
+ }
+ })
+ }
+}
diff --git a/tests/robustness/coverage/coverage_test.go b/tests/robustness/coverage/coverage_test.go
new file mode 100644
index 000000000000..2bae6415c6e1
--- /dev/null
+++ b/tests/robustness/coverage/coverage_test.go
@@ -0,0 +1,593 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 coverage_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/olekukonko/tablewriter"
+ "github.com/olekukonko/tablewriter/tw"
+ traceservice "go.opentelemetry.io/proto/otlp/collector/trace/v1"
+ tracev1 "go.opentelemetry.io/proto/otlp/trace/v1"
+ "google.golang.org/protobuf/encoding/protojson"
+)
+
+type column struct {
+ name string
+
+ // matcher encodes column with boolean values.
+ matcher Matcher
+}
+
+type method struct {
+ name string
+
+ // matcher should be a tight filter that returns true only if the method was
+ // used to make a call to Etcd (that produced the matching trace).
+ matcher Matcher
+}
+
+type refOp struct {
+ // args splits traces into buckets based on Matcher or Modeler results.
+ args []column
+ // methods tries to associate Matched with method name.
+ methods []method
+
+ keyAttrName string
+}
+
+type Row struct {
+ Method, Pattern, Args string
+}
+
+const notMatched byte = ' '
+
+type leaseRow struct {
+ SumUses int
+ SumTTL int
+ Calls int
+}
+
+var referenceUsageOfWatchAndKV = map[string]refOp{
+ "etcdserverpb.KV/Range": {
+ args: []column{
+ {name: "limit", matcher: isLimitSet},
+ {name: "rangeEnd", matcher: isRangeEndSet},
+ {name: "rev", matcher: isRevisionSet},
+ {name: "countOnly", matcher: isCountOnly},
+ {name: "keysOnly", matcher: isKeysOnly},
+ },
+ keyAttrName: "range_begin",
+ methods: []method{
+ {
+ name: "Healthcheck",
+ matcher: keyIsEqualStr("range_begin", "/registry/health"),
+ },
+ {
+ name: "Compaction",
+ matcher: keyIsEqualStr("range_begin", "compact_rev_key"),
+ },
+ {
+ name: "Get",
+ matcher: andMatcher(
+ keyIsEqualInt("limit", 1),
+ notMatcher(isRangeEndSet),
+ ),
+ },
+ {
+ name: "Count",
+ matcher: andMatcher(
+ isCountOnly,
+ isRangeEndSet,
+ notMatcher(orMatcher(isKeysOnly, isLimitSet, isRevisionSet)),
+ ),
+ },
+ {
+ name: "Keys",
+ matcher: andMatcher(
+ isKeysOnly,
+ isRangeEndSet,
+ notMatcher(orMatcher(isCountOnly, isLimitSet, isRevisionSet)),
+ ),
+ },
+ {
+ name: "List",
+ matcher: andMatcher(
+ isRangeEndSet,
+ notMatcher(orMatcher(isKeysOnly, isCountOnly)),
+ ),
+ },
+ {
+ name: "GetCurrentRevision",
+ matcher: andMatcher(
+ keyIsEqualInt("limit", 1),
+ isRangeEndSet,
+ notMatcher(orMatcher(isKeysOnly, isCountOnly, isRevisionSet)),
+ ),
+ },
+ },
+ },
+ "etcdserverpb.KV/Txn": {
+ args: []column{
+ {name: "getOnFailure", matcher: keyIsEqualInt("failure_len", 1)},
+ {name: "readOnly", matcher: isReadOnly},
+ {name: "lease", matcher: intAttrSet("success_first_lease")},
+ },
+ keyAttrName: "compare_first_key",
+ methods: []method{
+ {
+ name: "Compaction",
+ matcher: keyIsEqualStr("compare_first_key", "compact_rev_key"),
+ },
+ {
+ name: "OptimisticPut",
+ matcher: andMatcher(
+ keyIsEqualInt("compare_len", 1),
+ keyIsEqualInt("success_len", 1),
+ keyIsEqualStr("success_first_type", "put"),
+ notMatcher(isReadOnly),
+ ),
+ },
+ {
+ name: "OptimisticDelete",
+ matcher: andMatcher(
+ keyIsEqualInt("compare_len", 1),
+ keyIsEqualInt("success_len", 1),
+ keyIsEqualStr("success_first_type", "delete_range"),
+ notMatcher(isReadOnly),
+ ),
+ },
+ },
+ },
+ "etcdserverpb.KV/Compact": {
+ args: []column{
+ {name: "rev", matcher: isRevisionSet},
+ {name: "physical", matcher: boolAttrSet("is_physical")},
+ },
+ methods: []method{
+ {name: "Compact", matcher: all},
+ },
+ },
+ "etcdserverpb.Watch/Watch": {
+ args: []column{
+ {name: "range_end", matcher: isRangeEndSet},
+ {name: "start_rev", matcher: intAttrSet("start_rev")},
+ {name: "prev_kv", matcher: boolAttrSet("prev_kv")},
+ {name: "fragment", matcher: boolAttrSet("fragment")},
+ {name: "progress_notify", matcher: boolAttrSet("progress_notify")},
+ },
+ keyAttrName: "key",
+ methods: []method{
+ {name: "Compaction", matcher: keyIsEqualStr("key", "compact_rev_key")},
+ {name: "Watch", matcher: andMatcher(
+ isRangeEndSet,
+ intAttrSet("start_rev"),
+ boolAttrSet("prev_kv"),
+ notMatcher(boolAttrSet("fragment")),
+ )},
+ },
+ },
+}
+
+func TestInterfaceUse(t *testing.T) {
+ files, err := os.ReadDir("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, file := range files {
+ filename := file.Name()
+ if filename == ".gitignore" {
+ continue
+ }
+ t.Run(filename, func(t *testing.T) { testInterfaceUse(t, filename) })
+ }
+}
+
+func testInterfaceUse(t *testing.T, filename string) {
+ b, err := os.ReadFile(filepath.Join("testdata", filename))
+ if err != nil {
+ t.Fatalf("read test data: %v", err)
+ }
+ var dump Dump
+ err = json.Unmarshal(b, &dump)
+ if err != nil {
+ t.Fatalf("unmarshal testdata %s: %v", filename, err)
+ }
+ if dump.Result == nil {
+ t.Fatalf("missing result data")
+ }
+ spansByID := spansMap(t, dump.Result.GetResourceSpans())
+ callsByOperationName, countsByGRPC := callsMap(spansByID)
+ t.Logf("\n%s", printableCallTable(countsByGRPC))
+ spansByLeaseID := leaseMap(callsByOperationName)
+
+ t.Run("only_expected_methods_were_called", func(t *testing.T) {
+ expectedEtcdMethodsCalled := map[string]bool{
+ // All calls should go through etcd-k8s interface
+ "etcdserverpb.KV/Range": true,
+ "etcdserverpb.KV/Txn": true,
+ // Not part of the contract interface (yet)
+ "etcdserverpb.Watch/Watch": true,
+ // Compaction should move to using internal Etcd mechanism
+ // Discussed in https://github.com/kubernetes/kubernetes/issues/80513
+ "etcdserverpb.KV/Compact": true,
+ // Used to manage masterleases and events
+ "etcdserverpb.Lease/LeaseGrant": true,
+ // Used to expose database size on apiserver's metrics endpoint
+ "etcdserverpb.Maintenance/Status": true,
+ }
+
+ for opName, count := range countsByGRPC {
+ if _, ok := expectedEtcdMethodsCalled[opName]; !ok {
+ t.Errorf("unexpected %d calls to method: %s", count, opName)
+ }
+ }
+ })
+ for op, td := range referenceUsageOfWatchAndKV {
+ t.Run(op, func(t *testing.T) {
+ if _, ok := countsByGRPC[op]; !ok {
+ t.Fatalf("expected %q method to be called at least once", op)
+ }
+ // tracesWithNoMethod ensures that we print error only once when a
+ // new call pattern is found.
+ tracesWithNoMethod := make(map[string]bool)
+
+ callCounts := make(map[Row]int)
+ contractCallCounts := make(map[Row]int)
+ for _, span := range callsByOperationName[op] {
+ args := columnsToArgs(span, td.args)
+
+ pattern, pFound := extractPattern(span, td.keyAttrName)
+ if !pFound && !tracesWithNoMethod[args] {
+ t.Errorf("New key pattern detected: %s", pattern)
+ tracesWithNoMethod[args] = true
+ }
+
+ method, mFound := extractMethod(td.methods, span)
+ if !mFound && !tracesWithNoMethod[args] {
+ t.Errorf("New call pattern detected: %s(key=%s,%s)", op, pattern, argsToDescription(args, td.args))
+ tracesWithNoMethod[args] = true
+ }
+
+ _, cFound := contract(spansByID, span)
+ if cFound {
+ contractCallCounts[Row{method, pattern, args}]++
+ }
+ callCounts[Row{method, pattern, args}]++
+ }
+
+ t.Logf("\n%s", printableMatcherTable(td.args, callCounts, contractCallCounts))
+ })
+ }
+
+ t.Run("etcdserverpb.Lease/LeaseGrant", func(t *testing.T) {
+ op := "etcdserverpb.Lease/LeaseGrant"
+ if _, ok := countsByGRPC[op]; !ok {
+ t.Fatalf("expected %q method to be called at least once", op)
+ }
+ callCounts := make(map[string]leaseRow)
+ for _, span := range callsByOperationName[op] {
+ leaseID, lFound := intAttr(span, "id")
+ if !lFound {
+ t.Errorf("Lease without ID: %v", span)
+ continue
+ }
+ txns := spansByLeaseID[leaseID]
+
+ pattern, pFound := extractPatternFromTxns(txns)
+ if !pFound {
+ t.Errorf("New key pattern detected: %s", pattern)
+ continue
+ }
+ row := callCounts[pattern]
+ ttl, found := intAttr(span, "ttl")
+ if !found {
+ t.Errorf("Lease without TTL: %v", span)
+ continue
+ }
+ row.SumTTL += ttl
+ row.SumUses += len(txns)
+ row.Calls++
+ callCounts[pattern] = row
+ }
+
+ t.Logf("\n%s", printableLeaseTable(callCounts))
+ })
+}
+
+func callsMap(spansByID map[string]*tracev1.Span) (map[string][]*tracev1.Span, map[string]int) {
+ // Add to map only spans that are direct children of Etcd grpc spans.
+ callsByOperationName := make(map[string][]*tracev1.Span)
+ grpcCounts := make(map[string]int)
+ for _, span := range spansByID {
+ if isEtcdGRPC(span) {
+ grpcCounts[span.GetName()]++
+ continue
+ }
+ parent, ok := spansByID[string(span.GetParentSpanId())]
+ if !ok || !isEtcdGRPC(parent) {
+ continue
+ }
+ opName := parent.GetName()
+ callsByOperationName[opName] = append(callsByOperationName[opName], span)
+ }
+ return callsByOperationName, grpcCounts
+}
+
+func spansMap(t *testing.T, traces []*tracev1.ResourceSpans) map[string]*tracev1.Span {
+ t.Helper()
+
+ // Mark all traces with at least one span recorded in apiserver.
+ inApiserver := make(map[string]bool)
+ for _, trace := range traces {
+ sn, sFound := serviceName(trace)
+ if !sFound {
+ t.Fatalf("resource span without service.name: %+v", trace)
+ }
+ if sn != "apiserver" {
+ continue
+ }
+ for _, scopeSpan := range trace.GetScopeSpans() {
+ for _, span := range scopeSpan.GetSpans() {
+ inApiserver[string(span.GetTraceId())] = true
+ }
+ }
+ }
+ if len(inApiserver) == 0 {
+ t.Logf("WARN: no records of traces from the apiserver")
+ }
+
+ // Map traces by their span ID.
+ spansByID := make(map[string]*tracev1.Span)
+ skipped := 0
+ for _, trace := range traces {
+ for _, scopeSpan := range trace.GetScopeSpans() {
+ for _, span := range scopeSpan.GetSpans() {
+ if len(inApiserver) > 0 && !inApiserver[string(span.GetTraceId())] {
+ skipped++
+ continue
+ }
+ id := string(span.GetSpanId())
+ if id == "" {
+ t.Fatalf("span without id: %+v", span)
+ }
+ spansByID[id] = span
+ }
+ }
+ }
+ if skipped > 0 {
+ t.Logf("WARN: skipped %d spans without traces in apiserver", skipped)
+ }
+ return spansByID
+}
+
+func leaseMap(callsByOperationName map[string][]*tracev1.Span) map[int][]*tracev1.Span {
+ ret := make(map[int][]*tracev1.Span)
+ for _, leaseSpan := range callsByOperationName["etcdserverpb.Lease/LeaseGrant"] {
+ leaseID, lFound := intAttr(leaseSpan, "id")
+ if !lFound {
+ continue
+ }
+ ret[leaseID] = nil
+ }
+ for _, txnSpan := range callsByOperationName["etcdserverpb.KV/Txn"] {
+ leaseID, lFound := intAttr(txnSpan, "success_first_lease")
+ if !lFound {
+ continue
+ }
+ ret[leaseID] = append(ret[leaseID], txnSpan)
+ }
+ return ret
+}
+
+func extractPatternFromTxns(txns []*tracev1.Span) (string, bool) {
+ patterns := make(map[string]int)
+ for _, txn := range txns {
+ key, kFound := strAttr(txn, "success_first_key")
+ if kFound {
+ p, pFound := pattern(key)
+ if !pFound {
+ return key, false
+ }
+ patterns[p]++
+ }
+ }
+ if len(patterns) > 1 {
+ return "multiple key patterns", false
+ }
+ for p := range patterns {
+ return p, true
+ }
+ return "no pattern found", false
+}
+
+func extractPattern(span *tracev1.Span, key string) (string, bool) {
+ if key == "" {
+ return "", true
+ }
+ k, found := strAttr(span, key)
+ if !found {
+ return "", false
+ }
+ return pattern(k)
+}
+
+func columnsToArgs(span *tracev1.Span, cols []column) string {
+ acc := make([]byte, len(cols))
+ for i, col := range cols {
+ if col.matcher(span) {
+ acc[i] = 'X'
+ } else {
+ acc[i] = notMatched
+ }
+ }
+ return string(acc)
+}
+
+func argsToDescription(matched string, cols []column) string {
+ ret := make([]string, len(cols))
+ for i, col := range cols {
+ ret[i] = fmt.Sprintf("%s=%v", col.name, matched[i] != notMatched)
+ }
+ return strings.Join(ret, ",")
+}
+
+func extractMethod(methodToMatched []method, span *tracev1.Span) (string, bool) {
+ for _, mm := range methodToMatched {
+ if mm.matcher(span) {
+ return mm.name, true
+ }
+ }
+ return "", false
+}
+
+type Traces struct {
+ traceservice.ExportTraceServiceRequest
+}
+
+func (t *Traces) UnmarshalJSON(b []byte) error {
+ return protojson.Unmarshal(b, &t.ExportTraceServiceRequest)
+}
+
+type Dump struct {
+ Result *Traces `json:"result"`
+}
+
+func printableCallTable(callsByOperationName map[string]int) string {
+ buf := new(bytes.Buffer)
+ cfgBuilder := tablewriter.NewConfigBuilder().WithRowAlignment(tw.AlignRight)
+ table := tablewriter.NewTable(buf, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header("method", "calls", "percent")
+
+ totalCalls := 0
+ for _, c := range callsByOperationName {
+ totalCalls += c
+ }
+
+ for opName, callCount := range callsByOperationName {
+ table.Append(opName, callCount, fmt.Sprintf("%.2f%%", float64(callCount*100)/float64(totalCalls)))
+ }
+ table.Footer("total", totalCalls, "100.00%")
+
+ table.Render()
+ return buf.String()
+}
+
+func printableMatcherTable(cols []column, res map[Row]int, contract map[Row]int) string {
+ keys := sortPatternTable(res)
+
+ buf := new(bytes.Buffer)
+ width := 2 + len(cols) + 3
+ alignment := make([]tw.Align, width)
+ alignment[1] = tw.AlignLeft
+ cfgBuilder := tablewriter.NewConfigBuilder().
+ WithRowAlignment(tw.AlignRight).
+ Row().Alignment().WithPerColumn(alignment).Build()
+ table := tablewriter.NewTable(buf, tablewriter.WithConfig(cfgBuilder.Build()))
+
+ hdr := make([]string, width)
+ hdr[0] = "method"
+ hdr[1] = "pattern"
+ for i, col := range cols {
+ hdr[i+2] = col.name
+ }
+ hdr[len(hdr)-3] = "contract"
+ hdr[len(hdr)-2] = "calls"
+ hdr[len(hdr)-1] = "percent"
+ table.Header(hdr)
+
+ totalCalls := 0
+ for _, c := range res {
+ totalCalls += c
+ }
+ totalContractCalls := 0
+ for _, c := range contract {
+ totalContractCalls += c
+ }
+
+ footer := make([]int, len(cols))
+ for _, r := range keys {
+ callCount := res[r]
+ rowPrefix := make([]string, len(cols))
+ for i := range cols {
+ rowPrefix[i] = string(r.Args[i])
+ if r.Args[i] != notMatched {
+ footer[i] += callCount
+ }
+ }
+
+ table.Append(append(
+ []string{r.Method, r.Pattern},
+ append(rowPrefix,
+ fmt.Sprintf("%.2f%%", float64(contract[r]*100)/float64(callCount)),
+ strconv.Itoa(callCount),
+ fmt.Sprintf("%.2f%%", float64(callCount*100)/float64(totalCalls)),
+ )...))
+ }
+
+ footerStr := make([]string, len(cols))
+ for i := range footer {
+ footerStr[i] = strconv.Itoa(footer[i])
+ }
+ table.Footer(append([]string{"", ""},
+ append(
+ footerStr,
+ strconv.Itoa(totalContractCalls),
+ strconv.Itoa(totalCalls),
+ "100.00%",
+ )...))
+
+ table.Render()
+ return buf.String()
+}
+
+func printableLeaseTable(callCounts map[string]leaseRow) string {
+ hdr := []string{"pattern", "avg uses", "avg ttl", "calls", "percent"}
+ buf := new(bytes.Buffer)
+ alignment := make([]tw.Align, len(hdr))
+ alignment[0] = tw.AlignLeft
+ cfgBuilder := tablewriter.NewConfigBuilder().
+ WithRowAlignment(tw.AlignRight).
+ Row().Alignment().WithPerColumn(alignment).Build()
+ table := tablewriter.NewTable(buf, tablewriter.WithConfig(cfgBuilder.Build()))
+ table.Header(hdr)
+
+ totalCalls := 0
+ for _, row := range callCounts {
+ totalCalls += row.Calls
+ }
+
+ for pattern, row := range callCounts {
+ table.Append(
+ pattern,
+ fmt.Sprintf("%.1f", float64(row.SumUses)/float64(row.Calls)),
+ fmt.Sprintf("%.1f", float64(row.SumTTL)/float64(row.Calls)),
+ strconv.Itoa(row.Calls),
+ fmt.Sprintf("%.2f%%", float64(row.Calls*100)/float64(totalCalls)),
+ )
+ }
+
+ table.Footer([]string{3: strconv.Itoa(totalCalls), 4: "100.00%"})
+ table.Render()
+ return buf.String()
+}
diff --git a/tests/robustness/coverage/key_pattern_test.go b/tests/robustness/coverage/key_pattern_test.go
new file mode 100644
index 000000000000..0ab8fd0e336a
--- /dev/null
+++ b/tests/robustness/coverage/key_pattern_test.go
@@ -0,0 +1,177 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 coverage_test
+
+import (
+ "strings"
+ "testing"
+)
+
+func pattern(key string) (string, bool) {
+ if key == "/registry/health" || key == "compact_rev_key" {
+ return key, true
+ }
+ key, valid := strings.CutPrefix(key, "/registry")
+ if !valid {
+ return key, false
+ }
+ slash := ""
+ if strings.HasSuffix(key, "/") {
+ slash = "/"
+ }
+ parts := strings.Split(strings.Trim(key, "/"), "/")
+ if len(parts[0]) == 0 {
+ return key, false
+ }
+
+ apiResource, shift := resourceOrCRD(parts[0])
+ parts = parts[shift:]
+
+ switch len(parts) {
+ case 0:
+ // Listing all resources.
+ return "/registry/" + apiResource + slash, true
+ case 1:
+ if slash == "" {
+ // Get on a non-namespaced resource.
+ return "/registry/" + apiResource + "/{name}", true
+ }
+ // Listing all resources in a namespace.
+ return "/registry/" + apiResource + "/{namespace}" + slash, true
+ case 2:
+ if slash == "" {
+ // Get on a namespaced resource.
+ return "/registry/" + apiResource + "/{namespace}/{name}", true
+ }
+ }
+
+ return key, false
+}
+
+func resourceOrCRD(s string) (string, int) {
+ if strings.Contains(s, ".") {
+ // Group name from the Custom Resource Definition.
+ // Names without dots are technically valid DNS name, but against best practices:
+ // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
+ // https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.33/#customresourcedefinitionspec-v1-apiextensions-k8s-io
+ return "{api-group}/{resource}", 2
+ }
+ switch s {
+ case "services":
+ // services have subresources specs and endpoints.
+ return "services/{subresource}", 2
+ case "masterleases":
+ // masterleases are not a regular resource.
+ return "masterleases", 1
+ case "events":
+ // events have watch cache disabled by default and use Etcd leases.
+ return "events", 1
+ }
+ return "{resource}", 1
+}
+
+func TestPatternValid(t *testing.T) {
+ for _, tc := range []struct {
+ key string
+ want string
+ }{
+ {
+ key: "compact_rev_key",
+ want: "compact_rev_key",
+ },
+ {
+ key: "/registry/health",
+ want: "/registry/health",
+ },
+ {
+ key: "/registry/resource/namespace/object",
+ want: "/registry/{resource}/{namespace}/{name}",
+ },
+ {
+ key: "/registry/resource/namespace_list/",
+ want: "/registry/{resource}/{namespace}/",
+ },
+ {
+ key: "/registry/services/specs/namespace/object",
+ want: "/registry/services/{subresource}/{namespace}/{name}",
+ },
+ {
+ key: "/registry/services/endpoints",
+ want: "/registry/services/{subresource}",
+ },
+ {
+ key: "/registry/services/specs/namespace_list/",
+ want: "/registry/services/{subresource}/{namespace}/",
+ },
+ {
+ key: "/registry/resource/object_get",
+ want: "/registry/{resource}/{name}",
+ },
+ {
+ key: "/registry/resource_rev",
+ want: "/registry/{resource}",
+ },
+ {
+ key: "/registry/resource_list/",
+ want: "/registry/{resource}/",
+ },
+ {
+ key: "/registry/crd.io/resource/namespace/object_get",
+ want: "/registry/{api-group}/{resource}/{namespace}/{name}",
+ },
+ {
+ key: "/registry/crd.io/resource/namespace_list/",
+ want: "/registry/{api-group}/{resource}/{namespace}/",
+ },
+ {
+ key: "/registry/crd.io/resource/object_get",
+ want: "/registry/{api-group}/{resource}/{name}",
+ },
+ {
+ key: "/registry/crd.io/resource_rev",
+ want: "/registry/{api-group}/{resource}",
+ },
+ {
+ key: "/registry/crd.io/resource_list/",
+ want: "/registry/{api-group}/{resource}/",
+ },
+ } {
+ name := strings.Trim(strings.ReplaceAll(tc.key, "/", "_"), "_")
+ t.Run(name, func(t *testing.T) {
+ got, valid := pattern(tc.key)
+ if !valid {
+ t.Fatalf("key=%q, want=valid, got=invalid", tc.key)
+ }
+ if got != tc.want {
+ t.Fatalf("key=%q, want=%s, got=%s", tc.key, tc.want, got)
+ }
+ })
+ }
+}
+
+func TestPatternInvalid(t *testing.T) {
+ for _, tc := range []string{
+ "/other-registry",
+ "/registry//",
+ } {
+ name := strings.Trim(strings.ReplaceAll(tc, "/", "_"), "_")
+ t.Run(name, func(t *testing.T) {
+ got, valid := pattern(tc)
+ if valid {
+ t.Fatalf("key=%q, want=invalid, got=valid, pattern=%q", tc, got)
+ }
+ })
+ }
+}
diff --git a/tests/robustness/coverage/kind-with-tracing.yaml b/tests/robustness/coverage/kind-with-tracing.yaml
new file mode 100644
index 000000000000..ec45c693194c
--- /dev/null
+++ b/tests/robustness/coverage/kind-with-tracing.yaml
@@ -0,0 +1,25 @@
+---
+kind: Cluster
+apiVersion: kind.x-k8s.io/v1alpha4
+nodes:
+ - role: control-plane
+ extraMounts:
+ - hostPath: apiserver-shared-conf
+ containerPath: /apiserver-shared-conf
+ readOnly: true
+ kubeadmConfigPatches:
+ - |
+ kind: ClusterConfiguration
+ etcd:
+ external:
+ endpoints:
+ - http://192.168.32.1:2379
+ apiServer:
+ extraArgs:
+ tracing-config-file: "/apiserver-shared-conf/tracing.yaml"
+ extraVolumes:
+ - name: tracing
+ hostPath: "/apiserver-shared-conf"
+ mountPath: "/apiserver-shared-conf"
+ - role: worker
+ - role: worker
diff --git a/tests/robustness/coverage/matchers_test.go b/tests/robustness/coverage/matchers_test.go
new file mode 100644
index 000000000000..45eb87beff08
--- /dev/null
+++ b/tests/robustness/coverage/matchers_test.go
@@ -0,0 +1,141 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 coverage_test
+
+import (
+ "strings"
+
+ tracev1 "go.opentelemetry.io/proto/otlp/trace/v1"
+)
+
+// Matcher returns true if span passes the filter.
+type Matcher func(span *tracev1.Span) bool
+
+func serviceName(trace *tracev1.ResourceSpans) (string, bool) {
+ for _, attr := range trace.GetResource().GetAttributes() {
+ if attr.GetKey() == "service.name" {
+ return attr.GetValue().GetStringValue(), true
+ }
+ }
+ return "", false
+}
+
+func isEtcdGRPC(span *tracev1.Span) bool {
+ return strings.HasPrefix(span.GetName(), "etcdserverpb.") && span.GetKind() == tracev1.Span_SPAN_KIND_SERVER
+}
+
+//nolint:unparam
+func keyIsEqualInt(key string, want int) Matcher {
+ return func(span *tracev1.Span) bool {
+ got, found := intAttr(span, key)
+ return found && got == want
+ }
+}
+
+var (
+ isLimitSet = intAttrSet("limit")
+ isRevisionSet = intAttrSet("rev")
+)
+
+func intAttrSet(key string) Matcher {
+ return func(span *tracev1.Span) bool {
+ val, found := intAttr(span, key)
+ return found && val > 0
+ }
+}
+
+func intAttr(span *tracev1.Span, key string) (int, bool) {
+ for _, attr := range span.GetAttributes() {
+ if attr.GetKey() == key {
+ return int(attr.GetValue().GetIntValue()), true
+ }
+ }
+ return 0, false
+}
+
+func keyIsEqualStr(key string, want string) Matcher {
+ return func(span *tracev1.Span) bool {
+ got, found := strAttr(span, key)
+ return found && got == want
+ }
+}
+
+func isRangeEndSet(span *tracev1.Span) bool {
+ rangeEnd, found := strAttr(span, "range_end")
+ return found && len(rangeEnd) > 0
+}
+
+func strAttr(span *tracev1.Span, key string) (string, bool) {
+ for _, attr := range span.GetAttributes() {
+ if attr.GetKey() == key {
+ return attr.Value.GetStringValue(), true
+ }
+ }
+ return "", false
+}
+
+var (
+ isReadOnly = boolAttrSet("read_only")
+ isKeysOnly = boolAttrSet("keys_only")
+ isCountOnly = boolAttrSet("count_only")
+)
+
+func boolAttrSet(key string) Matcher {
+ return func(span *tracev1.Span) bool {
+ val, found := boolAttr(span, key)
+ return found && val
+ }
+}
+
+func boolAttr(span *tracev1.Span, key string) (bool, bool) {
+ for _, attr := range span.GetAttributes() {
+ if attr.GetKey() == key {
+ return attr.Value.GetBoolValue(), true
+ }
+ }
+ return false, false
+}
+
+func orMatcher(l ...Matcher) Matcher {
+ return func(span *tracev1.Span) bool {
+ for _, m := range l {
+ if m(span) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+func andMatcher(l ...Matcher) Matcher {
+ return func(span *tracev1.Span) bool {
+ for _, m := range l {
+ if !m(span) {
+ return false
+ }
+ }
+ return true
+ }
+}
+
+func notMatcher(m Matcher) Matcher {
+ return func(span *tracev1.Span) bool {
+ return !m(span)
+ }
+}
+
+func all(_ *tracev1.Span) bool {
+ return true
+}
diff --git a/tests/robustness/coverage/patches/kubernetes/0001-Add-Open-Telemetry-trace-event-when-passing-through-.patch b/tests/robustness/coverage/patches/kubernetes/0001-Add-Open-Telemetry-trace-event-when-passing-through-.patch
new file mode 100644
index 000000000000..e88b9e3e8ad2
--- /dev/null
+++ b/tests/robustness/coverage/patches/kubernetes/0001-Add-Open-Telemetry-trace-event-when-passing-through-.patch
@@ -0,0 +1,75 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aleksander Mistewicz
+Date: Wed, 13 Aug 2025 13:45:20 +0200
+Subject: [PATCH 1/4] Add Open Telemetry trace event when passing through
+ contract interface
+
+Signed-off-by: Aleksander Mistewicz
+
+diff --git a/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go b/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
+index 11f2a456447..0efab3711d7 100644
+--- a/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
++++ b/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
+@@ -21,6 +21,8 @@ import (
+ pb "go.etcd.io/etcd/api/v3/etcdserverpb"
+ "go.etcd.io/etcd/api/v3/mvccpb"
+ clientv3 "go.etcd.io/etcd/client/v3"
++ "go.opentelemetry.io/otel/attribute"
++ "go.opentelemetry.io/otel/trace"
+ )
+
+ // New creates Client from config.
+@@ -45,6 +47,10 @@ type Client struct {
+ var _ Interface = (*Client)(nil)
+
+ func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetResponse, err error) {
++ trace.SpanFromContext(ctx).AddEvent("contract.Get", trace.WithAttributes(
++ attribute.String("key", key),
++ attribute.Int64("rev", opts.Revision),
++ ))
+ rangeResp, err := k.KV.Get(ctx, key, clientv3.WithRev(opts.Revision), clientv3.WithLimit(1))
+ if err != nil {
+ return resp, err
+@@ -57,6 +63,10 @@ func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetR
+ }
+
+ func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp ListResponse, err error) {
++ trace.SpanFromContext(ctx).AddEvent("contract.List", trace.WithAttributes(
++ attribute.String("prefix", prefix),
++ attribute.Int64("rev", opts.Revision),
++ ))
+ rangeStart := prefix
+ if opts.Continue != "" {
+ rangeStart = opts.Continue
+@@ -73,6 +83,9 @@ func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp
+ }
+
+ func (k Client) Count(ctx context.Context, prefix string, _ CountOptions) (int64, error) {
++ trace.SpanFromContext(ctx).AddEvent("contract.Count", trace.WithAttributes(
++ attribute.String("prefix", prefix),
++ ))
+ resp, err := k.KV.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithCountOnly())
+ if err != nil {
+ return 0, err
+@@ -81,6 +94,10 @@ func (k Client) Count(ctx context.Context, prefix string, _ CountOptions) (int64
+ }
+
+ func (k Client) OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts PutOptions) (resp PutResponse, err error) {
++ trace.SpanFromContext(ctx).AddEvent("contract.OptimisticPut", trace.WithAttributes(
++ attribute.String("key", key),
++ attribute.Int64("expected_rev", expectedRevision),
++ ))
+ txn := k.KV.Txn(ctx).If(
+ clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
+ ).Then(
+@@ -107,6 +124,10 @@ func (k Client) OptimisticPut(ctx context.Context, key string, value []byte, exp
+ }
+
+ func (k Client) OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts DeleteOptions) (resp DeleteResponse, err error) {
++ trace.SpanFromContext(ctx).AddEvent("contract.OptimisticDelete", trace.WithAttributes(
++ attribute.String("key", key),
++ attribute.Int64("expected_rev", expectedRevision),
++ ))
+ txn := k.KV.Txn(ctx).If(
+ clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
+ ).Then(
diff --git a/tests/robustness/coverage/patches/kubernetes/0002-Add-kubernetesEtcdContractTracker.patch b/tests/robustness/coverage/patches/kubernetes/0002-Add-kubernetesEtcdContractTracker.patch
new file mode 100644
index 000000000000..bd36b7129f80
--- /dev/null
+++ b/tests/robustness/coverage/patches/kubernetes/0002-Add-kubernetesEtcdContractTracker.patch
@@ -0,0 +1,113 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aleksander Mistewicz
+Date: Mon, 18 Aug 2025 12:32:16 +0200
+Subject: [PATCH 2/4] Add kubernetesEtcdContractTracker
+
+This decorator will make it easier to ensure that all calls to the
+underlying storage that go through the contract interface produce
+spans. TracerProvider needs to be passed to the contract tracker to
+ensure that all spans will be correctly exported.
+
+Signed-off-by: Aleksander Mistewicz
+
+diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/kubernetes_contract_tracker.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/kubernetes_contract_tracker.go
+new file mode 100644
+index 00000000000..c16eecb20fa
+--- /dev/null
++++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/kubernetes_contract_tracker.go
+@@ -0,0 +1,73 @@
++/*
++Copyright 2025 The Kubernetes Authors.
++
++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 etcd3
++
++import (
++ "context"
++
++ "go.etcd.io/etcd/client/v3/kubernetes"
++ "go.opentelemetry.io/otel/attribute"
++ "go.opentelemetry.io/otel/trace"
++)
++
++const instrumentationScope = "k8s.io/apiserver/pkg/storage/etcd3"
++
++func NewKubernetesEtcdContractTracker(delegate kubernetes.Interface, tp trace.TracerProvider) kubernetes.Interface {
++ return &kubernetesEtcdContractTracker{Interface: delegate, tracer: tp.Tracer(instrumentationScope)}
++}
++
++type kubernetesEtcdContractTracker struct {
++ kubernetes.Interface
++
++ tracer trace.Tracer
++}
++
++func (k *kubernetesEtcdContractTracker) Get(ctx context.Context, key string, opts kubernetes.GetOptions) (kubernetes.GetResponse, error) {
++ ctx, span := k.tracer.Start(ctx, "Get kubernetesEtcdContract",
++ trace.WithAttributes(attribute.String("key", key), attribute.Int("rev", int(opts.Revision))))
++ defer span.End()
++ return k.Interface.Get(ctx, key, opts)
++}
++
++func (k *kubernetesEtcdContractTracker) List(ctx context.Context, prefix string, opts kubernetes.ListOptions) (kubernetes.ListResponse, error) {
++ ctx, span := k.tracer.Start(ctx, "List kubernetesEtcdContract",
++ trace.WithAttributes(attribute.String("key", prefix), attribute.Int("rev", int(opts.Revision)), attribute.Int("limit", int(opts.Limit))))
++ defer span.End()
++ return k.Interface.List(ctx, prefix, opts)
++}
++
++func (k *kubernetesEtcdContractTracker) Count(ctx context.Context, prefix string, opts kubernetes.CountOptions) (int64, error) {
++ ctx, span := k.tracer.Start(ctx, "Count kubernetesEtcdContract",
++ trace.WithNewRoot(), // Count is called periodically from the same context, so it would show up as a single trace otherwise
++ trace.WithAttributes(attribute.String("key", prefix)))
++ defer span.End()
++ return k.Interface.Count(ctx, prefix, opts)
++}
++
++func (k *kubernetesEtcdContractTracker) OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts kubernetes.PutOptions) (kubernetes.PutResponse, error) {
++ ctx, span := k.tracer.Start(ctx, "OptimisticPut kubernetesEtcdContract",
++ trace.WithAttributes(attribute.String("key", key), attribute.Int("rev", int(expectedRevision)), attribute.Int("lease", int(opts.LeaseID)), attribute.Bool("get_on_failure", opts.GetOnFailure)))
++ defer span.End()
++ return k.Interface.OptimisticPut(ctx, key, value, expectedRevision, opts)
++}
++
++func (k *kubernetesEtcdContractTracker) OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts kubernetes.DeleteOptions) (kubernetes.DeleteResponse, error) {
++ ctx, span := k.tracer.Start(ctx, "OptimisticDelete kubernetesEtcdContract",
++ trace.WithAttributes(attribute.String("key", key), attribute.Int("rev", int(expectedRevision)), attribute.Bool("get_on_failure", opts.GetOnFailure)))
++ defer span.End()
++ return k.Interface.OptimisticDelete(ctx, key, expectedRevision, opts)
++}
+diff --git a/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go b/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go
+index 6b60601a784..235850a44f2 100644
+--- a/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go
++++ b/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd3.go
+@@ -354,8 +354,16 @@ var newETCD3Client = func(c storagebackend.TransportConfig) (*kubernetes.Client,
+ TLS: tlsConfig,
+ Logger: etcd3ClientLogger,
+ }
++ client, err := kubernetes.New(cfg)
++ if err != nil {
++ return nil, err
++ }
++ if c.TracerProvider != nil {
++ // Decorate the Kubernetes instance so all events added later will belong to short-lived Spans.
++ client.Kubernetes = etcd3.NewKubernetesEtcdContractTracker(client, c.TracerProvider)
++ }
+
+- return kubernetes.New(cfg)
++ return client, nil
+ }
+
+ type runningCompactor struct {
diff --git a/tests/robustness/coverage/patches/kubernetes/0003-Add-1m-timeout-to-Watch-to-ensure-Spans-are-exported.patch b/tests/robustness/coverage/patches/kubernetes/0003-Add-1m-timeout-to-Watch-to-ensure-Spans-are-exported.patch
new file mode 100644
index 000000000000..8d79533a6aed
--- /dev/null
+++ b/tests/robustness/coverage/patches/kubernetes/0003-Add-1m-timeout-to-Watch-to-ensure-Spans-are-exported.patch
@@ -0,0 +1,20 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aleksander Mistewicz
+Date: Fri, 22 Aug 2025 14:18:07 +0200
+Subject: [PATCH 3/4] Add 1m timeout to Watch to ensure Spans are exported
+
+Signed-off-by: Aleksander Mistewicz
+
+diff --git a/vendor/go.etcd.io/etcd/client/v3/watch.go b/vendor/go.etcd.io/etcd/client/v3/watch.go
+index a46f98b8e28..ac820cabbb1 100644
+--- a/vendor/go.etcd.io/etcd/client/v3/watch.go
++++ b/vendor/go.etcd.io/etcd/client/v3/watch.go
+@@ -273,7 +273,7 @@ func (vc *valCtx) Done() <-chan struct{} { return valCtxCh }
+ func (vc *valCtx) Err() error { return nil }
+
+ func (w *watcher) newWatcherGRPCStream(inctx context.Context) *watchGRPCStream {
+- ctx, cancel := context.WithCancel(&valCtx{inctx})
++ ctx, cancel := context.WithTimeout(&valCtx{inctx}, time.Minute)
+ wgs := &watchGRPCStream{
+ owner: w,
+ remote: w.remote,
diff --git a/tests/robustness/coverage/patches/kubernetes/0004-Configure-cmd-tests.patch b/tests/robustness/coverage/patches/kubernetes/0004-Configure-cmd-tests.patch
new file mode 100644
index 000000000000..5ca6eab6a35f
--- /dev/null
+++ b/tests/robustness/coverage/patches/kubernetes/0004-Configure-cmd-tests.patch
@@ -0,0 +1,56 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aleksander Mistewicz
+Date: Mon, 4 Aug 2025 15:56:48 +0200
+Subject: [PATCH 4/4] Configure cmd tests
+
+
+diff --git a/hack/lib/etcd.sh b/hack/lib/etcd.sh
+index a3316e73bd4..f6b6bea6e26 100755
+--- a/hack/lib/etcd.sh
++++ b/hack/lib/etcd.sh
+@@ -25,6 +25,8 @@ ETCD_PORT=${ETCD_PORT:-2379}
+ ETCD_LOGLEVEL=${ETCD_LOGLEVEL:-warn}
+ export KUBE_INTEGRATION_ETCD_URL="http://${ETCD_HOST}:${ETCD_PORT}"
+
++export PATH="${PATH}:/go/src/k8s.io/kubernetes/third_party/etcd"
++
+ kube::etcd::validate() {
+ # validate if in path
+ command -v etcd >/dev/null || {
+@@ -89,8 +91,8 @@ kube::etcd::start() {
+ return
+ fi
+
+- kube::log::info "etcd --advertise-client-urls ${KUBE_INTEGRATION_ETCD_URL} --data-dir ${ETCD_DIR} --listen-client-urls http://${ETCD_HOST}:${ETCD_PORT} --listen-peer-urls http://localhost:0 --log-level=${ETCD_LOGLEVEL} 2> \"${ETCD_LOGFILE}\" >/dev/null"
+- etcd --advertise-client-urls "${KUBE_INTEGRATION_ETCD_URL}" --data-dir "${ETCD_DIR}" --listen-client-urls "${KUBE_INTEGRATION_ETCD_URL}" --listen-peer-urls "http://localhost:0" --log-level="${ETCD_LOGLEVEL}" 2> "${ETCD_LOGFILE}" >/dev/null &
++ kube::log::info "etcd --advertise-client-urls ${KUBE_INTEGRATION_ETCD_URL} --data-dir ${ETCD_DIR} --listen-client-urls http://${ETCD_HOST}:${ETCD_PORT} --listen-peer-urls http://localhost:0 --log-level=${ETCD_LOGLEVEL} --enable-distributed-tracing --distributed-tracing-address=\"192.168.32.1:4317\" --distributed-tracing-service-name=\"etcd\" --distributed-tracing-sampling-rate=1000000 2> \"${ETCD_LOGFILE}\" >/dev/null"
++ etcd --advertise-client-urls "${KUBE_INTEGRATION_ETCD_URL}" --data-dir "${ETCD_DIR}" --listen-client-urls "${KUBE_INTEGRATION_ETCD_URL}" --listen-peer-urls "http://localhost:0" --log-level="${ETCD_LOGLEVEL}" --enable-distributed-tracing --distributed-tracing-address="192.168.32.1:4317" --distributed-tracing-service-name="etcd" --distributed-tracing-sampling-rate=1000000 2> "${ETCD_LOGFILE}" >/dev/null &
+ ETCD_PID=$!
+
+ echo "Waiting for etcd to come up."
+diff --git a/hack/make-rules/test-cmd.sh b/hack/make-rules/test-cmd.sh
+index 7d9fb1db65c..be3b95341bb 100755
+--- a/hack/make-rules/test-cmd.sh
++++ b/hack/make-rules/test-cmd.sh
+@@ -69,6 +69,13 @@ function run_kube_apiserver() {
+ VERSION_OVERRIDE="--version=$("${THIS_PLATFORM_BIN}/kube-apiserver" --version | awk '{print $2}')${CUSTOM_VERSION_SUFFIX:-}"
+ fi
+
++ cat < "/tmp/kube-tracing-file"
++apiVersion: apiserver.config.k8s.io/v1beta1
++kind: TracingConfiguration
++endpoint: 192.168.32.1:4317
++samplingRatePerMillion: 1000000
++EOF
++
+ "${THIS_PLATFORM_BIN}/kube-apiserver" \
+ ${VERSION_OVERRIDE:+"${VERSION_OVERRIDE}"} \
+ --bind-address="127.0.0.1" \
+@@ -84,6 +91,7 @@ function run_kube_apiserver() {
+ --service-account-issuer="https://kubernetes.default.svc" \
+ --service-account-signing-key-file="${SERVICE_ACCOUNT_KEY}" \
+ --storage-media-type="${KUBE_TEST_API_STORAGE_TYPE-}" \
++ --tracing-config-file="/tmp/kube-tracing-file" \
+ --cert-dir="${TMPDIR:-/tmp/}" \
+ --service-cluster-ip-range="10.0.0.0/24" \
+ --client-ca-file=hack/testdata/ca/ca.crt \
diff --git a/tests/robustness/coverage/sort_test.go b/tests/robustness/coverage/sort_test.go
new file mode 100644
index 000000000000..7c65fba90dc2
--- /dev/null
+++ b/tests/robustness/coverage/sort_test.go
@@ -0,0 +1,66 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 coverage_test
+
+import (
+ "maps"
+ "slices"
+ "strings"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+func sortPatternTable(res map[Row]int) []Row {
+ keys := slices.Collect(maps.Keys(res))
+ slices.SortFunc(keys, func(a, b Row) int {
+ if a.Pattern != b.Pattern {
+ return strings.Compare(a.Pattern, b.Pattern)
+ }
+ if a.Method != b.Method {
+ return strings.Compare(a.Method, b.Method)
+ }
+ return strings.Compare(a.Args, b.Args)
+ })
+ return keys
+}
+
+func TestSortPatternTable(t *testing.T) {
+ want := []Row{
+ {Pattern: "/registry/events/", Method: "", Args: ""},
+ {Pattern: "/registry/events/{namespace}/", Method: "List", Args: ""},
+ {Pattern: "/registry/events/{namespace}/{name}", Method: "Get", Args: ""},
+ {Pattern: "/registry/health", Method: "Healthcheck", Args: ""},
+ {Pattern: "/registry/masterleases/", Method: "List", Args: ""},
+ {Pattern: "/registry/masterleases/{name}", Method: "Get", Args: ""},
+ {Pattern: "/registry/{api-group}/{resource}/", Method: "List", Args: "XX"},
+ {Pattern: "/registry/{api-group}/{resource}/", Method: "List", Args: "XXX"},
+ {Pattern: "/registry/{api-group}/{resource}/{name}", Method: "Get", Args: ""},
+ {Pattern: "/registry/{resource}", Method: "Get", Args: ""},
+ {Pattern: "/registry/{resource}/", Method: "List", Args: ""},
+ {Pattern: "/registry/{resource}/{namespace}/{name}", Method: "Get", Args: ""},
+ {Pattern: "/registry/{resource}/{name}", Method: "Get", Args: ""},
+ {Pattern: "compact_rev_key", Method: "Compaction", Args: ""},
+ }
+ res := make(map[Row]int, len(want))
+ for _, r := range want {
+ res[r] = 0
+ }
+ got := sortPatternTable(res)
+ if diff := cmp.Diff(want, got); diff != "" {
+ t.Logf("\n+%#v", got)
+ t.Errorf("Sort mismatch (-want +got):\n%s", diff)
+ }
+}
diff --git a/tests/robustness/coverage/testdata/.gitignore b/tests/robustness/coverage/testdata/.gitignore
new file mode 100644
index 000000000000..c96a04f008ee
--- /dev/null
+++ b/tests/robustness/coverage/testdata/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/tests/robustness/failpoint/cluster.go b/tests/robustness/failpoint/cluster.go
index eb619036875e..e72c54a1b2df 100644
--- a/tests/robustness/failpoint/cluster.go
+++ b/tests/robustness/failpoint/cluster.go
@@ -236,7 +236,7 @@ func (f memberDowngradeUpgrade) Inject(ctx context.Context, t *testing.T, lg *za
// NOTE: By default, the leader can cancel the downgrade once all members
// have reached the target version. However, determining the final stable
// cluster version after an upgrade can be challenging. To ensure stability,
- // we should wait for leader to cancel downgrade process.
+ // we should wait for the leader to cancel the downgrade process.
e2e.AssertProcessLogs(t, clus.Procs[clus.WaitLeader(t)], "the cluster has been downgraded")
// partial upgrade the cluster
diff --git a/tests/robustness/failpoint/gofail.go b/tests/robustness/failpoint/gofail.go
index 3ce2ff39a535..ac23be5fe706 100644
--- a/tests/robustness/failpoint/gofail.go
+++ b/tests/robustness/failpoint/gofail.go
@@ -194,7 +194,7 @@ func (f killAndGofailSleep) Inject(ctx context.Context, t *testing.T, lg *zap.Lo
if err != nil {
return nil, err
}
- // TODO: Check gofail status (https://github.com/etcd-io/gofail/pull/47) and wait for sleep to beis executed at least once.
+ // TODO: Check gofail status (https://github.com/etcd-io/gofail/pull/47) and wait for sleep to be executed at least once.
return nil, nil
}
diff --git a/tests/robustness/failpoint/trigger.go b/tests/robustness/failpoint/trigger.go
index 9ae9a8084819..44ed69f439ec 100644
--- a/tests/robustness/failpoint/trigger.go
+++ b/tests/robustness/failpoint/trigger.go
@@ -75,31 +75,34 @@ func (t triggerCompact) Trigger(ctx context.Context, _ *testing.T, member e2e.Et
}
rev = resp.Header.Revision
- if !t.multiBatchCompaction || rev > int64(clus.Cfg.ServerConfig.ExperimentalCompactionBatchLimit) {
+ if !t.multiBatchCompaction || rev > int64(clus.Cfg.ServerConfig.CompactionBatchLimit) {
break
}
time.Sleep(50 * time.Millisecond)
}
_, err = cc.Compact(ctx, rev)
+
+ reports := []report.ClientReport{cc.Report()}
+
if err != nil && !connectionError(err) {
- return nil, fmt.Errorf("failed to compact: %w", err)
+ return reports, fmt.Errorf("failed to compact: %w", err)
}
- return []report.ClientReport{cc.Report()}, nil
+ return reports, nil
}
func (t triggerCompact) Available(config e2e.EtcdProcessClusterConfig, _ e2e.EtcdProcess, profile traffic.Profile) bool {
- if profile.ForbidCompaction {
+ if profile.Compaction != nil {
return false
}
- // Since introduction of compaction into traffic, injecting compaction failpoints started interfeering with peer proxy.
+ // Since the introduction of compaction into traffic, injecting compaction failpoints started interfering with the peer proxy.
// TODO: Re-enable the peer proxy for compact failpoints when we confirm the root cause.
if config.PeerProxy {
return false
}
// For multiBatchCompaction we need to guarantee that there are enough revisions between two compaction requests.
- // With addition of compaction requests to traffic this might be hard if experimental-compaction-batch-limit is too high.
+ // With addition of compaction requests to traffic this might be hard if -compaction-batch-limit is too high.
if t.multiBatchCompaction {
- return config.ServerConfig.ExperimentalCompactionBatchLimit <= 10
+ return config.ServerConfig.CompactionBatchLimit <= 10
}
return true
}
diff --git a/tests/robustness/identity/id.go b/tests/robustness/identity/id.go
index 149544853912..74e070df66cb 100644
--- a/tests/robustness/identity/id.go
+++ b/tests/robustness/identity/id.go
@@ -17,11 +17,11 @@ package identity
import "sync/atomic"
type Provider interface {
- // NewStreamID returns an integer starting from zero to make it render nicely by porcupine visualization.
+ // NewStreamID returns an integer starting from zero to make it render nicely by the porcupine visualization.
NewStreamID() int
- // NewRequestID returns unique identification used to make write requests unique.
+ // NewRequestID returns a unique identification used to make write requests unique.
NewRequestID() int
- // NewClientID returns unique identification for client and their reports.
+ // NewClientID returns a unique identification for client and their reports.
NewClientID() int
}
diff --git a/tests/robustness/main_test.go b/tests/robustness/main_test.go
index 2adb53241409..3a55ab397290 100644
--- a/tests/robustness/main_test.go
+++ b/tests/robustness/main_test.go
@@ -16,7 +16,12 @@ package robustness
import (
"context"
+ "fmt"
"math/rand"
+ "os"
+ "path/filepath"
+ "slices"
+ "strings"
"testing"
"time"
@@ -30,7 +35,6 @@ import (
"go.etcd.io/etcd/tests/v3/robustness/client"
"go.etcd.io/etcd/tests/v3/robustness/failpoint"
"go.etcd.io/etcd/tests/v3/robustness/identity"
- "go.etcd.io/etcd/tests/v3/robustness/model"
"go.etcd.io/etcd/tests/v3/robustness/report"
"go.etcd.io/etcd/tests/v3/robustness/scenarios"
"go.etcd.io/etcd/tests/v3/robustness/traffic"
@@ -41,7 +45,7 @@ var testRunner = framework.E2eTestRunner
var (
WaitBeforeFailpoint = time.Second
- WaitJitter = traffic.DefaultCompactionPeriod
+ WaitJitter = time.Millisecond * 200
WaitAfterFailpoint = time.Second
)
@@ -55,7 +59,7 @@ func TestRobustnessExploratory(t *testing.T) {
t.Run(s.Name, func(t *testing.T) {
lg := zaptest.NewLogger(t)
s.Cluster.Logger = lg
- ctx := context.Background()
+ ctx := t.Context()
c, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithConfig(&s.Cluster))
require.NoError(t, err)
defer forcestopCluster(c)
@@ -74,7 +78,7 @@ func TestRobustnessRegression(t *testing.T) {
t.Run(s.Name, func(t *testing.T) {
lg := zaptest.NewLogger(t)
s.Cluster.Logger = lg
- ctx := context.Background()
+ ctx := t.Context()
c, err := e2e.NewEtcdProcessCluster(ctx, t, e2e.WithConfig(&s.Cluster))
require.NoError(t, err)
defer forcestopCluster(c)
@@ -84,21 +88,37 @@ func TestRobustnessRegression(t *testing.T) {
}
func testRobustness(ctx context.Context, t *testing.T, lg *zap.Logger, s scenarios.TestScenario, c *e2e.EtcdProcessCluster) {
- r := report.TestReport{Logger: lg, Cluster: c}
+ r, err := report.NewTestReport(lg, testResultsDirectory(t), report.ServerDataPaths(c), &report.TrafficDetail{ExpectUniqueRevision: s.Traffic.ExpectUniqueRevision()})
+ require.NoError(t, err)
// t.Failed() returns false during panicking. We need to forcibly
// save data on panicking.
// Refer to: https://github.com/golang/go/issues/49929
panicked := true
defer func() {
- r.Report(t, panicked)
+ err = r.Finalize(t.Failed(), panicked)
+ if err != nil {
+ t.Error(err)
+ }
}()
- r.Client = runScenario(ctx, t, s, lg, c)
- persistedRequests, err := report.PersistedRequestsCluster(lg, c)
- require.NoError(t, err)
+ clientReports := runScenario(ctx, t, s, lg, c)
+ r.SetClientReports(clientReports)
+ err = r.SaveEtcdData()
+ if err != nil {
+ t.Error(err)
+ }
validateConfig := validate.Config{ExpectRevisionUnique: s.Traffic.ExpectUniqueRevision()}
- r.Visualize = validate.ValidateAndReturnVisualize(t, lg, validateConfig, r.Client, persistedRequests, 5*time.Minute)
-
+ persistedRequests, err := report.PersistedRequestsCluster(lg, c)
+ if err != nil {
+ t.Error(err)
+ }
+ result := validate.ValidateAndReturnVisualize(lg, validateConfig, clientReports, persistedRequests, 5*time.Minute)
+ err = result.Error()
+ if err != nil {
+ t.Error(err)
+ } else {
+ r.SetVisualizer(result.Linearization.Visualize)
+ }
panicked = false
}
@@ -106,7 +126,7 @@ func runScenario(ctx context.Context, t *testing.T, s scenarios.TestScenario, lg
ctx, cancel := context.WithCancel(ctx)
defer cancel()
g := errgroup.Group{}
- var operationReport, watchReport, failpointClientReport []report.ClientReport
+ var failpointClientReport []report.ClientReport
failpointInjected := make(chan report.FailpointInjection, 1)
// using baseTime time-measuring operation to get monotonic clock reading
@@ -130,40 +150,45 @@ func runScenario(ctx context.Context, t *testing.T, s scenarios.TestScenario, lg
}
return nil
})
+ trafficSet := client.NewSet(ids, baseTime)
+ defer trafficSet.Close()
maxRevisionChan := make(chan int64, 1)
g.Go(func() error {
defer close(maxRevisionChan)
- operationReport = traffic.SimulateTraffic(ctx, t, lg, clus, s.Profile, s.Traffic, failpointInjected, baseTime, ids)
- maxRevision := operationsMaxRevision(operationReport)
+ operationReport := traffic.SimulateTraffic(ctx, t, lg, clus, s.Profile, s.Traffic, failpointInjected, trafficSet)
+ maxRevision := report.OperationsMaxRevision(operationReport)
maxRevisionChan <- maxRevision
lg.Info("Finished simulating Traffic", zap.Int64("max-revision", maxRevision))
return nil
})
+ watchSet := client.NewSet(ids, baseTime)
+ defer watchSet.Close()
g.Go(func() error {
- watchReport = client.CollectClusterWatchEvents(ctx, t, clus, maxRevisionChan, s.Watch, baseTime, ids)
- return nil
+ endpoints := processEndpoints(clus)
+ err := client.CollectClusterWatchEvents(ctx, client.CollectClusterWatchEventsParam{
+ Lg: lg,
+ Endpoints: endpoints,
+ MaxRevisionChan: maxRevisionChan,
+ ClientSet: watchSet,
+ })
+ return err
})
- g.Wait()
- return append(operationReport, append(failpointClientReport, watchReport...)...)
+ err := g.Wait()
+ if err != nil {
+ t.Error(err)
+ }
+
+ err = client.CheckEndOfTestHashKV(ctx, clus)
+ if err != nil {
+ t.Error(err)
+ }
+ return slices.Concat(trafficSet.Reports(), watchSet.Reports(), failpointClientReport)
}
func randomizeTime(base time.Duration, jitter time.Duration) time.Duration {
return base - jitter + time.Duration(rand.Int63n(int64(jitter)*2))
}
-func operationsMaxRevision(reports []report.ClientReport) int64 {
- var maxRevision int64
- for _, r := range reports {
- for _, op := range r.KeyValue {
- resp := op.Output.(model.MaybeEtcdResponse)
- if resp.Revision > maxRevision {
- maxRevision = resp.Revision
- }
- }
- }
- return maxRevision
-}
-
// forcestopCluster stops the etcd member with signal kill.
func forcestopCluster(clus *e2e.EtcdProcessCluster) error {
for _, member := range clus.Procs {
@@ -171,3 +196,30 @@ func forcestopCluster(clus *e2e.EtcdProcessCluster) error {
}
return clus.ConcurrentStop()
}
+
+func testResultsDirectory(t *testing.T) string {
+ resultsDirectory, ok := os.LookupEnv("RESULTS_DIR")
+ if !ok {
+ resultsDirectory = "/tmp/"
+ }
+ resultsDirectory, err := filepath.Abs(resultsDirectory)
+ if err != nil {
+ panic(err)
+ }
+ path, err := filepath.Abs(filepath.Join(
+ resultsDirectory, strings.ReplaceAll(t.Name(), "/", "_"), fmt.Sprintf("%v", time.Now().UnixNano())))
+ require.NoError(t, err)
+ err = os.RemoveAll(path)
+ require.NoError(t, err)
+ err = os.MkdirAll(path, 0o700)
+ require.NoError(t, err)
+ return path
+}
+
+func processEndpoints(clus *e2e.EtcdProcessCluster) []string {
+ endpoints := make([]string, 0, len(clus.Procs))
+ for _, proc := range clus.Procs {
+ endpoints = append(endpoints, proc.EndpointsGRPC()[0])
+ }
+ return endpoints
+}
diff --git a/tests/robustness/model/describe.go b/tests/robustness/model/describe.go
index 43eb58c06bdc..529103e5d5cd 100644
--- a/tests/robustness/model/describe.go
+++ b/tests/robustness/model/describe.go
@@ -18,6 +18,7 @@ import (
"fmt"
"strings"
+ "go.etcd.io/etcd/client/pkg/v3/types"
clientv3 "go.etcd.io/etcd/client/v3"
)
@@ -39,12 +40,9 @@ func describeEtcdResponse(request EtcdRequest, response MaybeEtcdResponse) strin
return fmt.Sprintf("%s, rev: %d", describeRangeResponse(request.Range.RangeOptions, *response.Range), response.Revision)
case Txn:
return fmt.Sprintf("%s, rev: %d", describeTxnResponse(request.Txn, response.Txn), response.Revision)
- case LeaseGrant, LeaseRevoke, Defragment:
- if response.Revision == 0 {
- return "ok"
- }
+ case LeaseGrant, LeaseRevoke:
return fmt.Sprintf("ok, rev: %d", response.Revision)
- case Compact:
+ case Compact, Defragment:
return "ok"
default:
return fmt.Sprintf("", request.Type)
@@ -82,6 +80,57 @@ func describeEtcdRequest(request EtcdRequest) string {
}
}
+func describeEtcdState(state EtcdState) string {
+ descHTML := make([]string, 0)
+
+ descHTML = append(descHTML, fmt.Sprintf("state, rev: %d, compactRev: %d
", state.Revision, state.CompactRevision))
+
+ keys := []string{}
+ for i, v := range state.KeyValues {
+ if v == nil {
+ continue
+ }
+ keys = append(keys, state.Keys[i])
+ }
+
+ if len(keys) > 0 {
+ descHTML = append(descHTML, "keys: ")
+
+ keys, values, leases := state.KeysValueLeases()
+ for i := range keys {
+ descHTML = append(descHTML, fmt.Sprintf("- %s - ", keys[i]))
+
+ value := values[i]
+ if value.Value.Value != "" {
+ descHTML = append(descHTML, fmt.Sprintf("val: %q, ", value.Value.Value))
+ }
+ if value.Value.Hash != 0 {
+ descHTML = append(descHTML, fmt.Sprintf("hash: %d, ", value.Value.Hash))
+ }
+ descHTML = append(descHTML, fmt.Sprintf("mod: %d, ver: %d", value.ModRevision, value.Version))
+ lease := leases[i]
+ if lease != 0 {
+ descHTML = append(descHTML, fmt.Sprintf(", lease: %d", lease))
+ }
+
+ descHTML = append(descHTML, "
")
+ }
+
+ descHTML = append(descHTML, "
")
+ }
+
+ leases := state.leases()
+ if len(leases) > 0 {
+ descHTML = append(descHTML, "leases: ")
+ for _, lease := range leases {
+ descHTML = append(descHTML, fmt.Sprintf("- %d
", lease))
+ }
+ descHTML = append(descHTML, "
")
+ }
+
+ return strings.Join(descHTML, "")
+}
+
func describeGuaranteedTxn(txn *TxnRequest) string {
if len(txn.Conditions) != 1 || len(txn.OperationsOnSuccess) != 1 || len(txn.OperationsOnFailure) > 1 {
return ""
@@ -170,6 +219,9 @@ func describeRangeRequest(opts RangeOptions, revision int64) string {
if opts.Limit != 0 {
kwargs = append(kwargs, fmt.Sprintf("limit=%d", opts.Limit))
}
+ if opts.KeysOnly {
+ kwargs = append(kwargs, "keysOnly")
+ }
kwargsString := strings.Join(kwargs, ", ")
if kwargsString != "" {
kwargsString = ", " + kwargsString
@@ -179,6 +231,8 @@ func describeRangeRequest(opts RangeOptions, revision int64) string {
return fmt.Sprintf("get(%q%s)", opts.Start, kwargsString)
case opts.End == clientv3.GetPrefixRangeEnd(opts.Start):
return fmt.Sprintf("list(%q%s)", opts.Start, kwargsString)
+ case strings.HasSuffix(opts.Start, "\x00") && strings.HasSuffix(opts.End, "0") && strings.HasPrefix(opts.Start, opts.End[:len(opts.End)-1]):
+ return fmt.Sprintf("list[continued](%q%s)", strings.TrimRight(opts.Start, "\x00"), kwargsString)
default:
return fmt.Sprintf("range(%q..%q%s)", opts.Start, opts.End, kwargsString)
}
@@ -201,7 +255,11 @@ func describeRangeResponse(request RangeOptions, response RangeResponse) string
if request.End != "" {
kvs := make([]string, len(response.KVs))
for i, kv := range response.KVs {
- kvs[i] = describeValueOrHash(kv.Value)
+ if request.KeysOnly {
+ kvs[i] = kv.Key
+ } else {
+ kvs[i] = fmt.Sprintf("%s:%s", kv.Key, describeValueOrHash(kv.Value))
+ }
}
return fmt.Sprintf("[%s], count: %d", strings.Join(kvs, ","), response.Count)
}
@@ -212,9 +270,16 @@ func describeRangeResponse(request RangeOptions, response RangeResponse) string
return describeValueOrHash(response.KVs[0].Value)
}
+func DescribeOperationMetadata(response MaybeEtcdResponse) string {
+ if response.MemberID != 0 {
+ return fmt.Sprintf("memberID: %s", types.ID(response.MemberID).String())
+ }
+ return ""
+}
+
func describeValueOrHash(value ValueOrHash) string {
if value.Hash != 0 {
- return fmt.Sprintf("hash: %d", value.Hash)
+ return fmt.Sprintf("", value.Hash)
}
if value.Value == "" {
return "nil"
diff --git a/tests/robustness/model/describe_test.go b/tests/robustness/model/describe_test.go
index 8ef2439ad8d7..af700de97f0e 100644
--- a/tests/robustness/model/describe_test.go
+++ b/tests/robustness/model/describe_test.go
@@ -18,9 +18,10 @@ import (
"errors"
"testing"
- "go.etcd.io/etcd/api/v3/mvccpb"
-
+ "github.com/anishathalye/porcupine"
"github.com/stretchr/testify/assert"
+
+ "go.etcd.io/etcd/api/v3/mvccpb"
)
func TestModelDescribe(t *testing.T) {
@@ -42,7 +43,7 @@ func TestModelDescribe(t *testing.T) {
{
req: getRequest("key2b"),
resp: getResponse("key2b", "01234567890123456789", 2, 2),
- expectDescribe: `get("key2b") -> hash: 2945867837, rev: 2`,
+ expectDescribe: `get("key2b") -> , rev: 2`,
},
{
req: putRequest("key3", "3"),
@@ -57,7 +58,7 @@ func TestModelDescribe(t *testing.T) {
{
req: putRequest("key3c", "01234567890123456789"),
resp: putResponse(3),
- expectDescribe: `put("key3c", hash: 2945867837) -> ok, rev: 3`,
+ expectDescribe: `put("key3c", ) -> ok, rev: 3`,
},
{
req: putRequest("key4", "4"),
@@ -126,8 +127,8 @@ func TestModelDescribe(t *testing.T) {
},
{
req: defragmentRequest(),
- resp: defragmentResponse(10),
- expectDescribe: `defragment() -> ok, rev: 10`,
+ resp: defragmentResponse(),
+ expectDescribe: `defragment() -> ok`,
},
{
req: listRequest("key11", 0),
@@ -136,13 +137,13 @@ func TestModelDescribe(t *testing.T) {
},
{
req: listRequest("key12", 0),
- resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("12")}}, 2, 12),
- expectDescribe: `list("key12") -> ["12"], count: 2, rev: 12`,
+ resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key12"), Value: []byte("12")}}, 2, 12),
+ expectDescribe: `list("key12") -> [key12:"12"], count: 2, rev: 12`,
},
{
req: listRequest("key13", 0),
- resp: rangeResponse([]*mvccpb.KeyValue{{Value: []byte("01234567890123456789")}}, 1, 13),
- expectDescribe: `list("key13") -> [hash: 2945867837], count: 1, rev: 13`,
+ resp: rangeResponse([]*mvccpb.KeyValue{{Key: []byte("key13"), Value: []byte("01234567890123456789")}}, 1, 13),
+ expectDescribe: `list("key13") -> [key13:], count: 1, rev: 13`,
},
{
req: listRequest("key14", 14),
@@ -170,7 +171,39 @@ func TestModelDescribe(t *testing.T) {
expectDescribe: `range("key16".."key16b", limit=2) -> [], count: 0, rev: 16`,
},
}
+ ops := []porcupine.Operation{}
+ for _, tc := range tcs {
+ ops = append(ops, porcupine.Operation{Input: tc.req, Output: tc.resp})
+ }
+ model := NonDeterministicModel(ModelKeys(ops))
+ for _, tc := range tcs {
+ assert.Equal(t, tc.expectDescribe, model.DescribeOperation(tc.req, tc.resp))
+ }
+}
+
+func TestDescribeOperationMetadata(t *testing.T) {
+ tcs := []struct {
+ resp MaybeEtcdResponse
+ expectDescribe string
+ }{
+ {
+ resp: MaybeEtcdResponse{},
+ expectDescribe: "",
+ },
+ {
+ resp: MaybeEtcdResponse{EtcdResponse: EtcdResponse{MemberID: 1}},
+ expectDescribe: "memberID: 1",
+ },
+ {
+ resp: MaybeEtcdResponse{EtcdResponse: EtcdResponse{MemberID: 100}},
+ expectDescribe: "memberID: 64",
+ },
+ {
+ resp: MaybeEtcdResponse{EtcdResponse: EtcdResponse{MemberID: 255}},
+ expectDescribe: "memberID: ff",
+ },
+ }
for _, tc := range tcs {
- assert.Equal(t, tc.expectDescribe, NonDeterministicModel.DescribeOperation(tc.req, tc.resp))
+ assert.Equal(t, tc.expectDescribe, DescribeOperationMetadata(tc.resp))
}
}
diff --git a/tests/robustness/model/deterministic.go b/tests/robustness/model/deterministic.go
index 9b61d13afd0c..feeed12e09f9 100644
--- a/tests/robustness/model/deterministic.go
+++ b/tests/robustness/model/deterministic.go
@@ -16,12 +16,11 @@ package model
import (
"encoding/json"
- "errors"
"fmt"
- "hash/fnv"
- "maps"
- "reflect"
+ "html"
+ "slices"
"sort"
+ "unsafe"
"github.com/anishathalye/porcupine"
@@ -29,49 +28,82 @@ import (
)
// DeterministicModel assumes a deterministic execution of etcd requests. All
-// requests that client called were executed and persisted by etcd. This
+// requests that the client called were executed and persisted by etcd. This
// assumption is good for simulating etcd behavior (aka writing a fake), but not
// for validating correctness as requests might be lost or interrupted. It
-// requires perfect knowledge of what happened to request which is not possible
+// requires perfect knowledge of what happened to a request, which is not possible
// in real systems.
//
-// Model can still respond with error or partial response.
+// Model can still respond with an error or partial response.
// - Error for etcd known errors, like future revision or compacted revision.
-// - Incomplete response when requests is correct, but model doesn't have all
-// to provide a full response. For example stale reads as model doesn't store
-// whole change history as real etcd does.
-var DeterministicModel = porcupine.Model{
- Init: func() any {
- data, err := json.Marshal(freshEtcdState())
- if err != nil {
- panic(err)
- }
- return string(data)
- },
- Step: func(st any, in any, out any) (bool, any) {
- var s EtcdState
- err := json.Unmarshal([]byte(st.(string)), &s)
- if err != nil {
- panic(err)
- }
- ok, s := s.apply(in.(EtcdRequest), out.(EtcdResponse))
- data, err := json.Marshal(s)
- if err != nil {
- panic(err)
- }
- return ok, string(data)
- },
- DescribeOperation: func(in, out any) string {
- return fmt.Sprintf("%s -> %s", describeEtcdRequest(in.(EtcdRequest)), describeEtcdResponse(in.(EtcdRequest), MaybeEtcdResponse{EtcdResponse: out.(EtcdResponse)}))
- },
+// - Incomplete response when the request is correct, but the model doesn't have all
+// the data to provide a full response. For example, stale reads as the model doesn't store
+// the whole change history as real etcd does.
+var DeterministicModel = func(keys []string) porcupine.Model {
+ return porcupine.Model{
+ Init: func() any {
+ return freshEtcdState(keys)
+ },
+ Step: func(st any, in any, out any) (bool, any) {
+ return st.(EtcdState).apply(in.(EtcdRequest), out.(EtcdResponse))
+ },
+ Equal: func(st1, st2 any) bool {
+ return st1.(EtcdState).Equal(st2.(EtcdState))
+ },
+ DescribeOperation: func(in, out any) string {
+ return fmt.Sprintf("%s -> %s", describeEtcdRequest(in.(EtcdRequest)), describeEtcdResponse(in.(EtcdRequest), MaybeEtcdResponse{EtcdResponse: out.(EtcdResponse)}))
+ },
+ DescribeOperationMetadata: func(info any) string {
+ if info == nil {
+ return ""
+ }
+ return DescribeOperationMetadata(MaybeEtcdResponse{EtcdResponse: info.(EtcdResponse)})
+ },
+ DescribeState: func(st any) string {
+ data, err := json.MarshalIndent(st, "", " ")
+ if err != nil {
+ panic(err)
+ }
+ return "" + html.EscapeString(string(data)) + "
"
+ },
+ }
}
type EtcdState struct {
- Revision int64
- CompactRevision int64
- KeyValues map[string]ValueRevision
- KeyLeases map[string]int64
- Leases map[int64]EtcdLease
+ Revision int64 `json:",omitempty"`
+ CompactRevision int64 `json:",omitempty"`
+ // Slices below are positionally aligned. If KeyValue is nil on index i,
+ // it means the key `Keys[i]` doesn't exist.
+ Keys []string `json:",omitempty"`
+ KeyValues []*ValueRevision `json:",omitempty"`
+ KeyLeases []*int64 `json:",omitempty"`
+ // All leases sorted by LeaseID.
+ Leases []int64 `json:",omitempty"`
+}
+
+func (s EtcdState) Equal(other EtcdState) bool {
+ if s.Revision != other.Revision {
+ return false
+ }
+ if s.CompactRevision != other.CompactRevision {
+ return false
+ }
+ if unsafe.SliceData(s.Keys) != unsafe.SliceData(other.Keys) {
+ panic("Can only compare states created from the same key slice")
+ }
+ return slices.EqualFunc(s.KeyValues, other.KeyValues, equalPtr) &&
+ slices.EqualFunc(s.KeyLeases, other.KeyLeases, equalPtr) &&
+ slices.Equal(s.Leases, other.Leases)
+}
+
+func equalPtr[T comparable](a, b *T) bool {
+ if a == b {
+ return true
+ }
+ if a == nil || b == nil {
+ return false
+ }
+ return *a == *b
}
func (s EtcdState) apply(request EtcdRequest, response EtcdResponse) (bool, EtcdState) {
@@ -85,144 +117,171 @@ func (s EtcdState) DeepCopy() EtcdState {
CompactRevision: s.CompactRevision,
}
- newState.KeyValues = maps.Clone(s.KeyValues)
- newState.KeyLeases = maps.Clone(s.KeyLeases)
-
- newLeases := map[int64]EtcdLease{}
- for key, val := range s.Leases {
- newLeases[key] = val.DeepCopy()
- }
- newState.Leases = newLeases
+ newState.Keys = s.Keys
+ newState.KeyValues = slices.Clone(s.KeyValues)
+ newState.KeyLeases = slices.Clone(s.KeyLeases)
+ newState.Leases = slices.Clone(s.Leases)
return newState
}
-func freshEtcdState() EtcdState {
+func freshEtcdState(keys []string) EtcdState {
return EtcdState{
Revision: 1,
// Start from CompactRevision equal -1 as etcd allows client to compact revision 0 for some reason.
CompactRevision: -1,
- KeyValues: map[string]ValueRevision{},
- KeyLeases: map[string]int64{},
- Leases: map[int64]EtcdLease{},
+ Keys: keys,
+ KeyValues: make([]*ValueRevision, len(keys)),
+ KeyLeases: make([]*int64, len(keys)),
+ Leases: make([]int64, 0),
}
}
// Step handles a successful request, returning updated state and response it would generate.
func (s EtcdState) Step(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
- newState := s.DeepCopy()
-
switch request.Type {
case Range:
- if request.Range.Revision == 0 || request.Range.Revision == newState.Revision {
- resp := newState.getRange(request.Range.RangeOptions)
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Range: &resp, Revision: newState.Revision}}
- }
- if request.Range.Revision > newState.Revision {
- return newState, MaybeEtcdResponse{Error: ErrEtcdFutureRev.Error()}
- }
- if request.Range.Revision < newState.CompactRevision {
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{ClientError: mvcc.ErrCompacted.Error()}}
- }
- return newState, MaybeEtcdResponse{Persisted: true, PersistedRevision: newState.Revision}
+ return s.stepRange(request)
case Txn:
- failure := false
- for _, cond := range request.Txn.Conditions {
- val := newState.KeyValues[cond.Key]
- if cond.ExpectedVersion > 0 {
- if val.Version != cond.ExpectedVersion {
- failure = true
- break
- }
- } else if val.ModRevision != cond.ExpectedRevision {
+ return s.stepTxn(request)
+ case LeaseGrant:
+ return s.stepLeaseGrant(request)
+ case LeaseRevoke:
+ return s.stepLeaseRevoke(request)
+ case Defragment:
+ return s.stepDefragment()
+ case Compact:
+ return s.stepCompact(request)
+ default:
+ panic(fmt.Sprintf("Unknown request type: %v", request.Type))
+ }
+}
+
+func (s EtcdState) stepRange(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
+ if request.Range.Revision == 0 || request.Range.Revision == s.Revision {
+ resp := s.getRange(request.Range.RangeOptions)
+ return s, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Range: &resp, Revision: s.Revision}}
+ }
+ if request.Range.Revision > s.Revision {
+ return s, MaybeEtcdResponse{Error: ErrEtcdFutureRev.Error()}
+ }
+ if request.Range.Revision < s.CompactRevision {
+ return s, MaybeEtcdResponse{EtcdResponse: EtcdResponse{ClientError: mvcc.ErrCompacted.Error()}}
+ }
+ return s, MaybeEtcdResponse{Persisted: true, PersistedRevision: s.Revision}
+}
+
+func (s EtcdState) stepTxn(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
+ // TODO: Avoid copying when TXN only has read operations
+ newState := s.DeepCopy()
+ failure := false
+ for _, cond := range request.Txn.Conditions {
+ val, ok := newState.GetValue(cond.Key)
+ if !ok {
+ val = &ValueRevision{}
+ }
+ if cond.ExpectedVersion > 0 {
+ if val.Version != cond.ExpectedVersion {
failure = true
break
}
+ } else if val.ModRevision != cond.ExpectedRevision {
+ failure = true
+ break
}
- operations := request.Txn.OperationsOnSuccess
- if failure {
- operations = request.Txn.OperationsOnFailure
- }
- opResp := make([]EtcdOperationResult, len(operations))
- increaseRevision := false
- for i, op := range operations {
- switch op.Type {
- case RangeOperation:
- opResp[i] = EtcdOperationResult{
- RangeResponse: newState.getRange(op.Range),
- }
- case PutOperation:
- _, leaseExists := newState.Leases[op.Put.LeaseID]
- if op.Put.LeaseID != 0 && !leaseExists {
+ }
+ operations := request.Txn.OperationsOnSuccess
+ if failure {
+ operations = request.Txn.OperationsOnFailure
+ }
+ opResp := make([]EtcdOperationResult, len(operations))
+ increaseRevision := false
+ for i, op := range operations {
+ switch op.Type {
+ case RangeOperation:
+ opResp[i] = EtcdOperationResult{
+ RangeResponse: newState.getRange(op.Range),
+ }
+ case PutOperation:
+ var leaseID *int64
+ if op.Put.LeaseID != 0 {
+ if !newState.leaseExists(op.Put.LeaseID) {
break
}
- ver := int64(1)
- if val, exists := newState.KeyValues[op.Put.Key]; exists && val.Version > 0 {
- ver = val.Version + 1
- }
- newState.KeyValues[op.Put.Key] = ValueRevision{
- Value: op.Put.Value,
- ModRevision: newState.Revision + 1,
- Version: ver,
- }
- increaseRevision = true
- newState = detachFromOldLease(newState, op.Put.Key)
- if leaseExists {
- newState = attachToNewLease(newState, op.Put.LeaseID, op.Put.Key)
- }
- case DeleteOperation:
- if _, ok := newState.KeyValues[op.Delete.Key]; ok {
- delete(newState.KeyValues, op.Delete.Key)
- increaseRevision = true
- newState = detachFromOldLease(newState, op.Delete.Key)
- opResp[i].Deleted = 1
- }
- default:
- panic("unsupported operation")
+ leaseID = &op.Put.LeaseID
}
- }
- if increaseRevision {
- newState.Revision++
- }
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Failure: failure, Results: opResp}, Revision: newState.Revision}}
- case LeaseGrant:
- lease := EtcdLease{
- LeaseID: request.LeaseGrant.LeaseID,
- Keys: map[string]struct{}{},
- }
- newState.Leases[request.LeaseGrant.LeaseID] = lease
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Revision: newState.Revision, LeaseGrant: &LeaseGrantReponse{}}}
- case LeaseRevoke:
- // Delete the keys attached to the lease
- keyDeleted := false
- for key := range newState.Leases[request.LeaseRevoke.LeaseID].Keys {
- // same as delete.
- if _, ok := newState.KeyValues[key]; ok {
- if !keyDeleted {
- keyDeleted = true
- }
- delete(newState.KeyValues, key)
- delete(newState.KeyLeases, key)
+ ver := int64(1)
+ if val, exists := newState.GetValue(op.Put.Key); exists && val.Version > 0 {
+ ver = val.Version + 1
}
+ newState.setValueLease(op.Put.Key, ValueRevision{
+ Value: op.Put.Value,
+ ModRevision: newState.Revision + 1,
+ Version: ver,
+ }, leaseID)
+ increaseRevision = true
+ case DeleteOperation:
+ if _, ok := newState.GetValue(op.Delete.Key); ok {
+ newState.deleteKey(op.Delete.Key)
+ increaseRevision = true
+ opResp[i].Deleted = 1
+ }
+ default:
+ panic("unsupported operation")
}
- // delete the lease
- delete(newState.Leases, request.LeaseRevoke.LeaseID)
- if keyDeleted {
- newState.Revision++
+ }
+ if increaseRevision {
+ newState.Revision++
+ }
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Failure: failure, Results: opResp}, Revision: newState.Revision}}
+}
+
+func (s EtcdState) stepLeaseGrant(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
+ newState := s.DeepCopy()
+ // Empty LeaseID means the request failed and client didn't get response. Ignore it as client cannot use lease without knowing its id.
+ if request.LeaseGrant.LeaseID == 0 {
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Revision: newState.Revision, LeaseGrant: &LeaseGrantResponse{}}}
+ }
+ newState.Leases = append(newState.Leases, request.LeaseGrant.LeaseID)
+ sort.Slice(newState.Leases, func(i, j int) bool { return newState.Leases[i] < newState.Leases[j] })
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Revision: newState.Revision, LeaseGrant: &LeaseGrantResponse{}}}
+}
+
+func (s EtcdState) stepLeaseRevoke(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
+ newState := s.DeepCopy()
+ keyDeleted := false
+ for i, l := range newState.KeyLeases {
+ if l != nil && *l == request.LeaseRevoke.LeaseID {
+ keyDeleted = true
+ newState.KeyValues[i] = nil
+ newState.KeyLeases[i] = nil
}
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Revision: newState.Revision, LeaseRevoke: &LeaseRevokeResponse{}}}
- case Defragment:
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Defragment: &DefragmentResponse{}, Revision: newState.Revision}}
- case Compact:
- if request.Compact.Revision <= newState.CompactRevision {
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{ClientError: mvcc.ErrCompacted.Error()}}
+ }
+ for i, l := range newState.Leases {
+ if l == request.LeaseRevoke.LeaseID {
+ newState.Leases = append(newState.Leases[:i], newState.Leases[i+1:]...)
+ break
}
- newState.CompactRevision = request.Compact.Revision
- // Set fake revision as compaction returns non-linearizable revision.
- // TODO: Model non-linearizable response revision in model.
- return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Compact: &CompactResponse{}, Revision: -1}}
- default:
- panic(fmt.Sprintf("Unknown request type: %v", request.Type))
}
+ if keyDeleted {
+ newState.Revision++
+ }
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Revision: newState.Revision, LeaseRevoke: &LeaseRevokeResponse{}}}
+}
+
+func (s EtcdState) stepDefragment() (EtcdState, MaybeEtcdResponse) {
+ return s, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Defragment: &DefragmentResponse{}, Revision: RevisionForNonLinearizableResponse}}
+}
+
+func (s EtcdState) stepCompact(request EtcdRequest) (EtcdState, MaybeEtcdResponse) {
+ newState := s.DeepCopy()
+ if request.Compact.Revision <= newState.CompactRevision {
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{ClientError: mvcc.ErrCompacted.Error()}}
+ }
+ if request.Compact.Revision > newState.Revision {
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{ClientError: mvcc.ErrFutureRev.Error()}}
+ }
+ newState.CompactRevision = request.Compact.Revision
+ return newState, MaybeEtcdResponse{EtcdResponse: EtcdResponse{Compact: &CompactResponse{}, Revision: RevisionForNonLinearizableResponse}}
}
func (s EtcdState) getRange(options RangeOptions) RangeResponse {
@@ -231,9 +290,17 @@ func (s EtcdState) getRange(options RangeOptions) RangeResponse {
}
if options.End != "" {
var count int64
- for k, v := range s.KeyValues {
+ for i, v := range s.KeyValues {
+ if v == nil {
+ continue
+ }
+ k := s.Keys[i]
if k >= options.Start && k < options.End {
- response.KVs = append(response.KVs, KeyValue{Key: k, ValueRevision: v})
+ kv := KeyValue{Key: k, ValueRevision: *v}
+ if options.KeysOnly {
+ kv.ValueRevision.Value = ValueOrHash{}
+ }
+ response.KVs = append(response.KVs, kv)
count++
}
}
@@ -245,11 +312,11 @@ func (s EtcdState) getRange(options RangeOptions) RangeResponse {
}
response.Count = count
} else {
- value, ok := s.KeyValues[options.Start]
+ value, ok := s.GetValue(options.Start)
if ok {
response.KVs = append(response.KVs, KeyValue{
Key: options.Start,
- ValueRevision: value,
+ ValueRevision: *value,
})
response.Count = 1
}
@@ -257,217 +324,76 @@ func (s EtcdState) getRange(options RangeOptions) RangeResponse {
return response
}
-func detachFromOldLease(s EtcdState, key string) EtcdState {
- if oldLeaseID, ok := s.KeyLeases[key]; ok {
- delete(s.Leases[oldLeaseID].Keys, key)
- delete(s.KeyLeases, key)
- }
- return s
-}
-
-func attachToNewLease(s EtcdState, leaseID int64, key string) EtcdState {
- s.KeyLeases[key] = leaseID
- s.Leases[leaseID].Keys[key] = leased
- return s
-}
-
-type RequestType string
-
-const (
- Range RequestType = "range"
- Txn RequestType = "txn"
- LeaseGrant RequestType = "leaseGrant"
- LeaseRevoke RequestType = "leaseRevoke"
- Defragment RequestType = "defragment"
- Compact RequestType = "compact"
-)
-
-type EtcdRequest struct {
- Type RequestType
- LeaseGrant *LeaseGrantRequest
- LeaseRevoke *LeaseRevokeRequest
- Range *RangeRequest
- Txn *TxnRequest
- Defragment *DefragmentRequest
- Compact *CompactRequest
-}
+func (s EtcdState) KeysValueLeases() (keys []string, values []ValueRevision, leases []int64) {
+ keys = make([]string, 0, len(s.KeyValues))
+ values = make([]ValueRevision, 0, len(s.KeyValues))
+ leases = make([]int64, 0, len(s.KeyLeases))
-func (r *EtcdRequest) IsRead() bool {
- if r.Type == Range {
- return true
- }
- if r.Type != Txn {
- return false
- }
- for _, op := range append(r.Txn.OperationsOnSuccess, r.Txn.OperationsOnFailure...) {
- if op.Type != RangeOperation {
- return false
+ for i, v := range s.KeyValues {
+ if v == nil {
+ continue
+ }
+ keys = append(keys, s.Keys[i])
+ values = append(values, *v)
+ lease := int64(0)
+ if s.KeyLeases[i] != nil {
+ lease = *s.KeyLeases[i]
}
+ leases = append(leases, lease)
}
- return true
-}
-
-type RangeRequest struct {
- RangeOptions
- Revision int64
-}
-
-type RangeOptions struct {
- Start string
- End string
- Limit int64
+ return keys, values, leases
}
-type PutOptions struct {
- Key string
- Value ValueOrHash
- LeaseID int64
+func (s EtcdState) leases() []int64 {
+ return slices.Clone(s.Leases)
}
-type DeleteOptions struct {
- Key string
-}
-
-type TxnRequest struct {
- Conditions []EtcdCondition
- OperationsOnSuccess []EtcdOperation
- OperationsOnFailure []EtcdOperation
-}
-
-type EtcdCondition struct {
- Key string
- ExpectedRevision int64
- ExpectedVersion int64
-}
-
-type EtcdOperation struct {
- Type OperationType
- Range RangeOptions
- Put PutOptions
- Delete DeleteOptions
-}
-
-type OperationType string
-
-const (
- RangeOperation OperationType = "range-operation"
- PutOperation OperationType = "put-operation"
- DeleteOperation OperationType = "delete-operation"
-)
-
-type LeaseGrantRequest struct {
- LeaseID int64
-}
-type LeaseRevokeRequest struct {
- LeaseID int64
-}
-type DefragmentRequest struct{}
-
-// MaybeEtcdResponse extends EtcdResponse to include partial information about responses to a request.
-// Possible response state information:
-// * Normal response. Client observed response. Only EtcdResponse is set.
-// * Persisted. Client didn't observe response, but we know it was persisted by etcd. Only Persisted is set
-// * Persisted with Revision. Client didn't observe response, but we know that it was persisted, and it's revision. Both Persisted and PersistedRevision is set.
-// * Error response. Client observed error, but we don't know if it was persisted. Only Error is set.
-type MaybeEtcdResponse struct {
- EtcdResponse
- Persisted bool
- PersistedRevision int64
- Error string
-}
-
-var ErrEtcdFutureRev = errors.New("future rev")
-
-type EtcdResponse struct {
- Txn *TxnResponse
- Range *RangeResponse
- LeaseGrant *LeaseGrantReponse
- LeaseRevoke *LeaseRevokeResponse
- Defragment *DefragmentResponse
- Compact *CompactResponse
- ClientError string
- Revision int64
-}
-
-func Match(r1, r2 MaybeEtcdResponse) bool {
- r1Revision := r1.Revision
- if r1.Persisted {
- r1Revision = r1.PersistedRevision
- }
- r2Revision := r2.Revision
- if r2.Persisted {
- r2Revision = r2.PersistedRevision
+func (s EtcdState) GetValue(key string) (*ValueRevision, bool) {
+ for i, k := range s.Keys {
+ if k == key {
+ return s.KeyValues[i], s.KeyValues[i] != nil
+ }
}
- return (r1.Persisted && r1.PersistedRevision == 0) || (r2.Persisted && r2.PersistedRevision == 0) || ((r1.Persisted || r2.Persisted) && (r1.Error != "" || r2.Error != "" || r1Revision == r2Revision)) || reflect.DeepEqual(r1, r2)
-}
-
-type TxnResponse struct {
- Failure bool
- Results []EtcdOperationResult
-}
-
-type RangeResponse struct {
- KVs []KeyValue
- Count int64
-}
-
-type LeaseGrantReponse struct {
- LeaseID int64
-}
-type (
- LeaseRevokeResponse struct{}
- DefragmentResponse struct{}
-)
-
-type EtcdOperationResult struct {
- RangeResponse
- Deleted int64
-}
-
-type KeyValue struct {
- Key string
- ValueRevision
+ return nil, false
}
-var leased = struct{}{}
-
-type EtcdLease struct {
- LeaseID int64
- Keys map[string]struct{}
-}
-
-func (el EtcdLease) DeepCopy() EtcdLease {
- return EtcdLease{
- LeaseID: el.LeaseID,
- Keys: maps.Clone(el.Keys),
+func (s EtcdState) leaseExists(lease int64) bool {
+ for _, l := range s.Leases {
+ if l == lease {
+ return true
+ }
}
+ return false
}
-type ValueRevision struct {
- Value ValueOrHash
- ModRevision int64
- Version int64
-}
-
-type ValueOrHash struct {
- Value string
- Hash uint32
+func (s EtcdState) setValueLease(key string, val ValueRevision, lease *int64) {
+ for i, k := range s.Keys {
+ if k == key {
+ s.KeyValues[i] = &val
+ s.KeyLeases[i] = lease
+ return
+ }
+ }
+ panic(fmt.Sprintf("couldn't find key %s in EtcdState (%v) when calling setValue", key, s.Keys))
}
-func ToValueOrHash(value string) ValueOrHash {
- v := ValueOrHash{}
- if len(value) < 20 {
- v.Value = value
- } else {
- h := fnv.New32a()
- h.Write([]byte(value))
- v.Hash = h.Sum32()
+func (s EtcdState) deleteKey(key string) {
+ for i, k := range s.Keys {
+ if k == key {
+ s.KeyValues[i] = nil
+ s.KeyLeases[i] = nil
+ return
+ }
}
- return v
+ panic(fmt.Sprintf("couldn't find key %s in EtcdState (%v) when calling setValue", key, s.Keys))
}
-type CompactResponse struct{}
-
-type CompactRequest struct {
- Revision int64
+func (s EtcdState) leaseKeys(leaseID int64) []string {
+ keys := []string{}
+ for i, l := range s.KeyLeases {
+ if l != nil && *l == leaseID {
+ keys = append(keys, s.Keys[i])
+ }
+ }
+ return keys
}
diff --git a/tests/robustness/model/deterministic_test.go b/tests/robustness/model/deterministic_test.go
index 6e75126239db..f5737e8859d0 100644
--- a/tests/robustness/model/deterministic_test.go
+++ b/tests/robustness/model/deterministic_test.go
@@ -15,11 +15,11 @@
package model
import (
- "encoding/json"
+ "math/rand"
+ "slices"
"testing"
"github.com/google/go-cmp/cmp"
- "github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/mvccpb"
)
@@ -28,15 +28,15 @@ func TestModelDeterministic(t *testing.T) {
for _, tc := range commonTestScenarios {
tc := tc
t.Run(tc.name, func(t *testing.T) {
- state := DeterministicModel.Init()
+ keys := keysFromTestOperations(tc.operations)
+ model := DeterministicModel(keys)
+ state := model.Init()
for _, op := range tc.operations {
- ok, newState := DeterministicModel.Step(state, op.req, op.resp.EtcdResponse)
+ ok, newState := model.Step(state, op.req, op.resp.EtcdResponse)
if op.expectFailure == ok {
t.Logf("state: %v", state)
- t.Errorf("Unexpected operation result, expect: %v, got: %v, operation: %s", !op.expectFailure, ok, DeterministicModel.DescribeOperation(op.req, op.resp.EtcdResponse))
- var loadedState EtcdState
- err := json.Unmarshal([]byte(state.(string)), &loadedState)
- require.NoErrorf(t, err, "Failed to load state")
+ t.Errorf("Unexpected operation result, expect: %v, got: %v, operation: %s", !op.expectFailure, ok, model.DescribeOperation(op.req, op.resp.EtcdResponse))
+ loadedState := state.(EtcdState)
_, resp := loadedState.Step(op.req)
t.Errorf("Response diff: %s", cmp.Diff(op.resp, resp))
break
@@ -50,6 +50,170 @@ func TestModelDeterministic(t *testing.T) {
}
}
+func TestEtcdStateEqual(t *testing.T) {
+ keys := []string{"key"}
+ testCases := []struct {
+ name string
+ s1 EtcdState
+ s2 EtcdState
+ equal bool
+ }{
+ {
+ name: "Fresh states should be equal",
+ s1: freshEtcdState(keys),
+ s2: freshEtcdState(keys),
+ equal: true,
+ },
+ {
+ name: "States from identical history should be equal",
+ s1: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "1"))
+ s, _ = s.Step(putRequest("key", "2"))
+ return s
+ }(),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "1"))
+ s, _ = s.Step(putRequest("key", "2"))
+ return s
+ }(),
+ equal: true,
+ },
+ {
+ name: "States from different history should not be equal",
+ s1: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "1"))
+ s, _ = s.Step(putRequest("key", "2"))
+ return s
+ }(),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "2"))
+ s, _ = s.Step(putRequest("key", "1"))
+ return s
+ }(),
+ equal: false,
+ },
+ {
+ name: "Empty states with higher revision should be equal",
+ s1: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "1"))
+ s, _ = s.Step(putRequest("key", "2"))
+ s, _ = s.Step(deleteRequest("key"))
+ return s
+ }(),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "2"))
+ s, _ = s.Step(putRequest("key", "1"))
+ s, _ = s.Step(deleteRequest("key"))
+ return s
+ }(),
+ equal: true,
+ },
+ {
+ name: "Grant and Revoke empty lease should be equal to fresh state",
+ s1: freshEtcdState(keys),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(leaseGrantRequest(1))
+ s, _ = s.Step(leaseRevokeRequest(1))
+ return s
+ }(),
+ equal: true,
+ },
+ {
+ name: "Delete via Revoke vs Delete directly should be equal",
+ s1: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(leaseGrantRequest(1))
+ s, _ = s.Step(putWithLeaseRequest("key", "val", 1))
+ s, _ = s.Step(leaseRevokeRequest(1))
+ return s
+ }(),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "val"))
+ s, _ = s.Step(deleteRequest("key"))
+ return s
+ }(),
+ equal: true,
+ },
+ {
+ name: "Put via Txn vs Put directly should be equal",
+ s1: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(compareRevisionAndPutRequest("key", 0, "val"))
+ return s
+ }(),
+ s2: func() EtcdState {
+ s := freshEtcdState(keys)
+ s, _ = s.Step(putRequest("key", "val"))
+ return s
+ }(),
+ equal: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ if tc.s1.Equal(tc.s2) != tc.equal {
+ t.Errorf("Expected equal=%v, got %v", tc.equal, !tc.equal)
+ t.Errorf("Diff:\n%v", cmp.Diff(tc.s1, tc.s2))
+ }
+ })
+ }
+}
+
+func TestEtcdStateEqualCommutativeRequests(t *testing.T) {
+ commutativeRequests := []EtcdRequest{
+ leaseGrantRequest(1),
+ leaseGrantRequest(2),
+ leaseRevokeRequest(3),
+ leaseRevokeRequest(4),
+ getRequest("key1"),
+ getRequest("key2"),
+ defragmentRequest(),
+ defragmentRequest(),
+ compactRequest(1),
+ compactRequest(2),
+ }
+ keys := []string{"key1", "key2"}
+
+ baseState := applyRequests(keys, commutativeRequests)
+
+ for i := 0; i < 10_000; i++ {
+ perm := slices.Clone(commutativeRequests)
+ rand.Shuffle(len(perm), func(i, j int) {
+ perm[i], perm[j] = perm[j], perm[i]
+ })
+ s2 := applyRequests(keys, perm)
+
+ if !baseState.Equal(s2) {
+ t.Errorf("Expected states to be equal after random reordering, but they are not")
+ }
+ }
+}
+
+func applyRequests(keys []string, reqs []EtcdRequest) EtcdState {
+ state := freshEtcdState(keys)
+ for _, req := range reqs {
+ state, _ = state.Step(req)
+ }
+ return state
+}
+
+func keysFromTestOperations(ops []testOperation) []string {
+ requests := make([]EtcdRequest, 0, len(ops))
+ for _, op := range ops {
+ requests = append(requests, op.req)
+ }
+ return keysFromRequests(requests)
+}
+
type modelTestCase struct {
name string
operations []testOperation
@@ -264,6 +428,13 @@ var commonTestScenarios = []modelTestCase{
{req: getRequest("key"), resp: getResponse("key", "2", 2, 2)},
},
},
+ {
+ name: "Lease grant returns current revision",
+ operations: []testOperation{
+ {req: putRequest("key", "1"), resp: putResponse(2)},
+ {req: leaseGrantRequest(1), resp: leaseGrantResponse(2)},
+ },
+ },
{
name: "Put with valid lease id should succeed. Put with expired lease id should fail",
operations: []testOperation{
@@ -381,27 +552,27 @@ var commonTestScenarios = []modelTestCase{
{req: getRequest("key"), resp: getResponse("key", "4", 4, 4)},
{req: compareRevisionAndPutRequest("key", 4, "5"), resp: compareRevisionAndPutResponse(true, 5)},
{req: deleteRequest("key"), resp: deleteResponse(1, 6)},
- {req: defragmentRequest(), resp: defragmentResponse(6)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
},
},
{
name: "Defragment success between all other request types",
operations: []testOperation{
- {req: defragmentRequest(), resp: defragmentResponse(1)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: leaseGrantRequest(1), resp: leaseGrantResponse(1)},
- {req: defragmentRequest(), resp: defragmentResponse(1)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: putWithLeaseRequest("key", "1", 1), resp: putResponse(2)},
- {req: defragmentRequest(), resp: defragmentResponse(2)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: leaseRevokeRequest(1), resp: leaseRevokeResponse(3)},
- {req: defragmentRequest(), resp: defragmentResponse(3)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: putRequest("key", "4"), resp: putResponse(4)},
- {req: defragmentRequest(), resp: defragmentResponse(4)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: getRequest("key"), resp: getResponse("key", "4", 4, 4)},
- {req: defragmentRequest(), resp: defragmentResponse(4)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: compareRevisionAndPutRequest("key", 4, "5"), resp: compareRevisionAndPutResponse(true, 5)},
- {req: defragmentRequest(), resp: defragmentResponse(5)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
{req: deleteRequest("key"), resp: deleteResponse(1, 6)},
- {req: defragmentRequest(), resp: defragmentResponse(6)},
+ {req: defragmentRequest(), resp: defragmentResponse()},
},
},
}
diff --git a/tests/robustness/model/history.go b/tests/robustness/model/history.go
index fc224a8afffe..a41ffeac82fb 100644
--- a/tests/robustness/model/history.go
+++ b/tests/robustness/model/history.go
@@ -28,44 +28,49 @@ import (
"go.etcd.io/etcd/tests/v3/robustness/identity"
)
-// AppendableHistory allows to collect history of sequential operations.
+// AppendableHistory allows collecting the history of sequential operations.
//
-// Ensures that operation history is compatible with porcupine library, by preventing concurrent requests sharing the
-// same stream id. For failed requests, we don't know their return time, so generate new stream id.
+// Ensures that the operation history is compatible with the porcupine library by preventing concurrent requests from sharing the
+// same stream id. For failed requests, we don't know their return time, so we generate a new stream id.
//
// Appending needs to be done in order of operation execution time (start, end time).
-// Operations time should be calculated as time.Since common base time to ensure that Go monotonic time is used.
+// Operation time should be calculated as time.Since a common base time to ensure that Go monotonic time is used.
// More in https://github.com/golang/go/blob/96add980ad27faed627f26ef1ab09e8fe45d6bd1/src/time/time.go#L10.
type AppendableHistory struct {
// streamID for the next operation. Used for porcupine.Operation.ClientId as porcupine assumes no concurrent requests.
streamID int
// If needed a new streamId is requested from idProvider.
idProvider identity.Provider
+ // lastOperation holds the last operation of each stream.
+ lastOperation map[int]*porcupine.Operation
History
}
func NewAppendableHistory(ids identity.Provider) *AppendableHistory {
return &AppendableHistory{
- streamID: ids.NewStreamID(),
- idProvider: ids,
+ streamID: ids.NewStreamID(),
+ idProvider: ids,
+ lastOperation: make(map[int]*porcupine.Operation),
History: History{
operations: []porcupine.Operation{},
},
}
}
-func (h *AppendableHistory) AppendRange(startKey, endKey string, revision, limit int64, start, end time.Duration, resp *clientv3.GetResponse, err error) {
- request := staleRangeRequest(startKey, endKey, limit, revision)
+func (h *AppendableHistory) AppendRange(startKey, endKey string, revision, limit int64, keysOnly bool, start, end time.Duration, resp *clientv3.GetResponse, err error) {
+ request := staleRangeRequest(startKey, endKey, limit, revision, keysOnly)
if err != nil {
h.appendFailed(request, start, end, err)
return
}
var respRevision int64
+ var respMemberID uint64
if resp != nil && resp.Header != nil {
respRevision = resp.Header.Revision
+ respMemberID = resp.Header.MemberId
}
- h.appendSuccessful(request, start, end, rangeResponse(resp.Kvs, resp.Count, respRevision))
+ h.appendSuccessful(request, start, end, rangeResponseWithMemberID(resp.Kvs, resp.Count, respRevision, MemberID(respMemberID)))
}
func (h *AppendableHistory) AppendPut(key, value string, start, end time.Duration, resp *clientv3.PutResponse, err error) {
@@ -75,10 +80,12 @@ func (h *AppendableHistory) AppendPut(key, value string, start, end time.Duratio
return
}
var revision int64
+ var memberID MemberID
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
+ memberID = MemberID(resp.Header.MemberId)
}
- h.appendSuccessful(request, start, end, putResponse(revision))
+ h.appendSuccessful(request, start, end, putResponseWithMemberID(revision, memberID))
}
func (h *AppendableHistory) AppendPutWithLease(key, value string, leaseID int64, start, end time.Duration, resp *clientv3.PutResponse, err error) {
@@ -88,10 +95,12 @@ func (h *AppendableHistory) AppendPutWithLease(key, value string, leaseID int64,
return
}
var revision int64
+ var memberID MemberID
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
+ memberID = MemberID(resp.Header.MemberId)
}
- h.appendSuccessful(request, start, end, putResponse(revision))
+ h.appendSuccessful(request, start, end, putResponseWithMemberID(revision, memberID))
}
func (h *AppendableHistory) AppendLeaseGrant(start, end time.Duration, resp *clientv3.LeaseGrantResponse, err error) {
@@ -105,10 +114,12 @@ func (h *AppendableHistory) AppendLeaseGrant(start, end time.Duration, resp *cli
return
}
var revision int64
+ var memberID MemberID
if resp != nil && resp.ResponseHeader != nil {
revision = resp.ResponseHeader.Revision
+ memberID = MemberID(resp.ResponseHeader.MemberId)
}
- h.appendSuccessful(request, start, end, leaseGrantResponse(revision))
+ h.appendSuccessful(request, start, end, leaseGrantResponseWithMemberID(revision, memberID))
}
func (h *AppendableHistory) AppendLeaseRevoke(id int64, start, end time.Duration, resp *clientv3.LeaseRevokeResponse, err error) {
@@ -118,10 +129,12 @@ func (h *AppendableHistory) AppendLeaseRevoke(id int64, start, end time.Duration
return
}
var revision int64
+ var memberID MemberID
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
+ memberID = MemberID(resp.Header.MemberId)
}
- h.appendSuccessful(request, start, end, leaseRevokeResponse(revision))
+ h.appendSuccessful(request, start, end, leaseRevokeResponseWithMemberID(revision, memberID))
}
func (h *AppendableHistory) AppendDelete(key string, start, end time.Duration, resp *clientv3.DeleteResponse, err error) {
@@ -131,12 +144,14 @@ func (h *AppendableHistory) AppendDelete(key string, start, end time.Duration, r
return
}
var revision int64
+ var memberID MemberID
var deleted int64
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
+ memberID = MemberID(resp.Header.MemberId)
deleted = resp.Deleted
}
- h.appendSuccessful(request, start, end, deleteResponse(deleted, revision))
+ h.appendSuccessful(request, start, end, deleteResponseWithMemberID(deleted, revision, memberID))
}
func (h *AppendableHistory) AppendTxn(cmp []clientv3.Cmp, clientOnSuccessOps, clientOnFailure []clientv3.Op, start, end time.Duration, resp *clientv3.TxnResponse, err error) {
@@ -158,14 +173,22 @@ func (h *AppendableHistory) AppendTxn(cmp []clientv3.Cmp, clientOnSuccessOps, cl
return
}
var revision int64
+ var memberID MemberID
if resp != nil && resp.Header != nil {
revision = resp.Header.Revision
+ memberID = MemberID(resp.Header.MemberId)
}
results := []EtcdOperationResult{}
for _, resp := range resp.Responses {
results = append(results, toEtcdOperationResult(resp))
}
- h.appendSuccessful(request, start, end, txnResponse(results, resp.Succeeded, revision))
+ h.appendSuccessful(request, start, end, txnResponseWithMemberID(results, resp.Succeeded, revision, memberID))
+}
+
+func (h *AppendableHistory) appendClientError(request EtcdRequest, start, end time.Duration, err error) {
+ h.appendSuccessful(request, start, end, MaybeEtcdResponse{
+ EtcdResponse: EtcdResponse{ClientError: err.Error()},
+ })
}
func (h *AppendableHistory) appendSuccessful(request EtcdRequest, start, end time.Duration, response MaybeEtcdResponse) {
@@ -175,20 +198,22 @@ func (h *AppendableHistory) appendSuccessful(request EtcdRequest, start, end tim
Call: start.Nanoseconds(),
Output: response,
Return: end.Nanoseconds(),
+ Metadata: response,
}
h.append(op)
}
func toEtcdCondition(cmp clientv3.Cmp) (cond EtcdCondition) {
+ compare := cmp.GetCompare()
switch {
- case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_MOD:
+ case compare.GetResult() == etcdserverpb.Compare_EQUAL && compare.GetTarget() == etcdserverpb.Compare_MOD:
cond.Key = string(cmp.KeyBytes())
- cond.ExpectedRevision = cmp.TargetUnion.(*etcdserverpb.Compare_ModRevision).ModRevision
- case cmp.Result == etcdserverpb.Compare_EQUAL && cmp.Target == etcdserverpb.Compare_VERSION:
- cond.ExpectedVersion = cmp.TargetUnion.(*etcdserverpb.Compare_Version).Version
+ cond.ExpectedRevision = compare.GetTargetUnion().(*etcdserverpb.Compare_ModRevision).ModRevision
+ case compare.GetResult() == etcdserverpb.Compare_EQUAL && compare.GetTarget() == etcdserverpb.Compare_VERSION:
+ cond.ExpectedVersion = compare.GetTargetUnion().(*etcdserverpb.Compare_Version).Version
cond.Key = string(cmp.KeyBytes())
default:
- panic(fmt.Sprintf("Compare not supported, target: %q, result: %q", cmp.Target, cmp.Result))
+ panic(fmt.Sprintf("Compare not supported, target: %q, result: %q", compare.GetTarget(), compare.GetResult()))
}
return cond
}
@@ -256,42 +281,38 @@ func (h *AppendableHistory) AppendDefragment(start, end time.Duration, resp *cli
h.appendFailed(request, start, end, err)
return
}
- var revision int64
- if resp != nil && resp.Header != nil {
- revision = resp.Header.Revision
- }
- h.appendSuccessful(request, start, end, defragmentResponse(revision))
+ h.appendSuccessful(request, start, end, defragmentResponse())
}
func (h *AppendableHistory) AppendCompact(rev int64, start, end time.Duration, resp *clientv3.CompactResponse, err error) {
request := compactRequest(rev)
if err != nil {
if strings.Contains(err.Error(), mvcc.ErrCompacted.Error()) {
- h.appendSuccessful(request, start, end, MaybeEtcdResponse{
- EtcdResponse: EtcdResponse{ClientError: mvcc.ErrCompacted.Error()},
- })
+ h.appendClientError(request, start, end, mvcc.ErrCompacted)
+ return
+ }
+ if strings.Contains(err.Error(), mvcc.ErrFutureRev.Error()) {
+ h.appendClientError(request, start, end, mvcc.ErrFutureRev)
return
}
h.appendFailed(request, start, end, err)
return
}
- // Set fake revision as compaction returns non-linearizable revision.
- // TODO: Model non-linearizable response revision in model.
- h.appendSuccessful(request, start, end, compactResponse(-1))
+ h.appendSuccessful(request, start, end, compactResponse())
}
func (h *AppendableHistory) appendFailed(request EtcdRequest, start, end time.Duration, err error) {
+ resp := failedResponse(err)
op := porcupine.Operation{
ClientId: h.streamID,
Input: request,
Call: start.Nanoseconds(),
- Output: failedResponse(err),
+ Output: resp,
Return: end.Nanoseconds(),
+ Metadata: resp,
}
isRead := request.IsRead()
if !isRead {
- // Failed writes can still be persisted, setting -1 for now as don't know when request has took effect.
- op.Return = -1
// Operations of single client needs to be sequential.
// As we don't know return time of failed operations, all new writes need to be done with new stream id.
h.streamID = h.idProvider.NewStreamID()
@@ -300,11 +321,11 @@ func (h *AppendableHistory) appendFailed(request EtcdRequest, start, end time.Du
}
func (h *AppendableHistory) append(op porcupine.Operation) {
- if op.Return != -1 && op.Call >= op.Return {
+ if op.Call >= op.Return {
panic(fmt.Sprintf("Invalid operation, call(%d) >= return(%d)", op.Call, op.Return))
}
- if len(h.operations) > 0 {
- prev := h.operations[len(h.operations)-1]
+
+ if prev, ok := h.lastOperation[op.ClientId]; ok {
if op.Call <= prev.Call {
panic(fmt.Sprintf("Out of order append, new.call(%d) <= prev.call(%d)", op.Call, prev.Call))
}
@@ -312,6 +333,8 @@ func (h *AppendableHistory) append(op porcupine.Operation) {
panic(fmt.Sprintf("Overlapping operations, new.call(%d) <= prev.return(%d)", op.Call, prev.Return))
}
}
+ h.lastOperation[op.ClientId] = &op
+
h.operations = append(h.operations, op)
}
@@ -320,11 +343,11 @@ func getRequest(key string) EtcdRequest {
}
func staleGetRequest(key string, revision int64) EtcdRequest {
- return staleRangeRequest(key, "", 0, revision)
+ return staleRangeRequest(key, "", 0, revision, false)
}
func rangeRequest(start, end string, limit int64) EtcdRequest {
- return staleRangeRequest(start, end, limit, 0)
+ return staleRangeRequest(start, end, limit, 0, false)
}
func listRequest(key string, limit int64) EtcdRequest {
@@ -332,11 +355,11 @@ func listRequest(key string, limit int64) EtcdRequest {
}
func staleListRequest(key string, limit, revision int64) EtcdRequest {
- return staleRangeRequest(key, clientv3.GetPrefixRangeEnd(key), limit, revision)
+ return staleRangeRequest(key, clientv3.GetPrefixRangeEnd(key), limit, revision, false)
}
-func staleRangeRequest(start, end string, limit, revision int64) EtcdRequest {
- return EtcdRequest{Type: Range, Range: &RangeRequest{RangeOptions: RangeOptions{Start: start, End: end, Limit: limit}, Revision: revision}}
+func staleRangeRequest(start, end string, limit, revision int64, keysOnly bool) EtcdRequest {
+ return EtcdRequest{Type: Range, Range: &RangeRequest{RangeOptions: RangeOptions{Start: start, End: end, Limit: limit, KeysOnly: keysOnly}, Revision: revision}}
}
func emptyGetResponse(revision int64) MaybeEtcdResponse {
@@ -352,6 +375,10 @@ func getResponseWithVer(key, value string, modRevision, ver, revision int64) May
}
func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtcdResponse {
+ return rangeResponseWithMemberID(kvs, count, revision, 0)
+}
+
+func rangeResponseWithMemberID(kvs []*mvccpb.KeyValue, count int64, revision int64, memberID MemberID) MaybeEtcdResponse {
result := RangeResponse{KVs: make([]KeyValue, len(kvs)), Count: count}
for i, kv := range kvs {
@@ -364,7 +391,7 @@ func rangeResponse(kvs []*mvccpb.KeyValue, count int64, revision int64) MaybeEtc
},
}
}
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Range: &result, Revision: revision}}
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Range: &result, Revision: revision, MemberID: memberID}}
}
func failedResponse(err error) MaybeEtcdResponse {
@@ -380,7 +407,11 @@ func putRequest(key, value string) EtcdRequest {
}
func putResponse(revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: []EtcdOperationResult{{}}}, Revision: revision}}
+ return putResponseWithMemberID(revision, 0)
+}
+
+func putResponseWithMemberID(revision int64, memberID MemberID) MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: []EtcdOperationResult{{}}}, Revision: revision, MemberID: memberID}}
}
func deleteRequest(key string) EtcdRequest {
@@ -388,7 +419,11 @@ func deleteRequest(key string) EtcdRequest {
}
func deleteResponse(deleted int64, revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: []EtcdOperationResult{{Deleted: deleted}}}, Revision: revision}}
+ return deleteResponseWithMemberID(deleted, revision, 0)
+}
+
+func deleteResponseWithMemberID(deleted int64, revision int64, memberID MemberID) MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: []EtcdOperationResult{{Deleted: deleted}}}, Revision: revision, MemberID: memberID}}
}
func compareRevisionAndPutRequest(key string, expectedRevision int64, value string) EtcdRequest {
@@ -439,7 +474,11 @@ func txnEmptyResponse(succeeded bool, revision int64) MaybeEtcdResponse {
}
func txnResponse(result []EtcdOperationResult, succeeded bool, revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: result, Failure: !succeeded}, Revision: revision}}
+ return txnResponseWithMemberID(result, succeeded, revision, 0)
+}
+
+func txnResponseWithMemberID(result []EtcdOperationResult, succeeded bool, revision int64, memberID MemberID) MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Txn: &TxnResponse{Results: result, Failure: !succeeded}, Revision: revision, MemberID: memberID}}
}
func putWithLeaseRequest(key, value string, leaseID int64) EtcdRequest {
@@ -451,7 +490,11 @@ func leaseGrantRequest(leaseID int64) EtcdRequest {
}
func leaseGrantResponse(revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{LeaseGrant: &LeaseGrantReponse{}, Revision: revision}}
+ return leaseGrantResponseWithMemberID(revision, 0)
+}
+
+func leaseGrantResponseWithMemberID(revision int64, memberID MemberID) MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{LeaseGrant: &LeaseGrantResponse{}, Revision: revision, MemberID: memberID}}
}
func leaseRevokeRequest(leaseID int64) EtcdRequest {
@@ -459,23 +502,27 @@ func leaseRevokeRequest(leaseID int64) EtcdRequest {
}
func leaseRevokeResponse(revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{LeaseRevoke: &LeaseRevokeResponse{}, Revision: revision}}
+ return leaseRevokeResponseWithMemberID(revision, 0)
+}
+
+func leaseRevokeResponseWithMemberID(revision int64, memberID MemberID) MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{LeaseRevoke: &LeaseRevokeResponse{}, Revision: revision, MemberID: memberID}}
}
func defragmentRequest() EtcdRequest {
return EtcdRequest{Type: Defragment, Defragment: &DefragmentRequest{}}
}
-func defragmentResponse(revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Defragment: &DefragmentResponse{}, Revision: revision}}
+func defragmentResponse() MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Defragment: &DefragmentResponse{}, Revision: RevisionForNonLinearizableResponse}}
}
func compactRequest(rev int64) EtcdRequest {
return EtcdRequest{Type: Compact, Compact: &CompactRequest{Revision: rev}}
}
-func compactResponse(revision int64) MaybeEtcdResponse {
- return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Compact: &CompactResponse{}, Revision: revision}}
+func compactResponse() MaybeEtcdResponse {
+ return MaybeEtcdResponse{EtcdResponse: EtcdResponse{Compact: &CompactResponse{}, Revision: RevisionForNonLinearizableResponse}}
}
type History struct {
@@ -488,34 +535,8 @@ func (h History) Len() int {
func (h History) Operations() []porcupine.Operation {
operations := make([]porcupine.Operation, 0, len(h.operations))
- maxTime := h.lastObservedTime()
- for _, op := range h.operations {
- // Failed requests don't have a known return time.
- if op.Return == -1 {
- // Simulate Infinity by using last observed time.
- op.Return = maxTime + time.Second.Nanoseconds()
- }
- operations = append(operations, op)
- }
- return operations
-}
-func (h History) lastObservedTime() int64 {
- var maxTime int64
- for _, op := range h.operations {
- if op.Return == -1 {
- // Collect call time from failed operations
- if op.Call > maxTime {
- maxTime = op.Call
- }
- } else {
- // Collect return time from successful operations
- if op.Return > maxTime {
- maxTime = op.Return
- }
- }
- }
- return maxTime
+ return append(operations, h.operations...)
}
func (h History) MaxRevision() int64 {
diff --git a/tests/robustness/model/history_test.go b/tests/robustness/model/history_test.go
new file mode 100644
index 000000000000..263441dca1f1
--- /dev/null
+++ b/tests/robustness/model/history_test.go
@@ -0,0 +1,245 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 model
+
+import (
+ "testing"
+
+ "github.com/anishathalye/porcupine"
+ "github.com/stretchr/testify/assert"
+
+ "go.etcd.io/etcd/tests/v3/robustness/identity"
+)
+
+func TestHistoryAppendSuccess(t *testing.T) {
+ tcs := []struct {
+ name string
+ operations []porcupine.Operation
+ }{
+ {
+ name: "append non-overlapping operations for the same clientId",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 100,
+ Output: nil,
+ Return: 200,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 2,
+ },
+ },
+ },
+ {
+ name: "append overlapping operations in different ClientId",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 2,
+ },
+ {
+ ClientId: 3,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 20,
+ },
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 101,
+ Output: nil,
+ Return: 200,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 3,
+ Output: nil,
+ Return: 4,
+ },
+ {
+ ClientId: 3,
+ Input: nil,
+ Call: 30,
+ Output: nil,
+ Return: 40,
+ },
+ },
+ },
+ }
+
+ for _, tc := range tcs {
+ h := NewAppendableHistory(identity.NewIDProvider())
+
+ for _, operation := range tc.operations[:len(tc.operations)-1] {
+ h.append(operation)
+ }
+ }
+}
+
+func TestHistoryAppendFailure(t *testing.T) {
+ tcs := []struct {
+ name string
+ operations []porcupine.Operation
+ expectError string
+ }{
+ {
+ name: "append operation with call time > return time",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 2,
+ Output: nil,
+ Return: 1,
+ },
+ },
+ expectError: "Invalid operation, call(2) >= return(1)",
+ },
+ {
+ name: "out of order append in the same ClientId",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 10,
+ },
+ },
+ expectError: "Out of order append, new.call(1) <= prev.call(10)",
+ },
+ {
+ name: "out of order append in one of the ClientIds",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 101,
+ Output: nil,
+ Return: 200,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 10,
+ },
+ },
+ expectError: "Out of order append, new.call(1) <= prev.call(10)",
+ },
+ {
+ name: "append overlapping operations in the same ClientId",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 20,
+ },
+ },
+ expectError: "Overlapping operations, new.call(10) <= prev.return(100)",
+ },
+ {
+ name: "append overlapping operations in one of the ClientIds",
+ operations: []porcupine.Operation{
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 1,
+ Input: nil,
+ Call: 101,
+ Output: nil,
+ Return: 200,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 1,
+ Output: nil,
+ Return: 100,
+ },
+ {
+ ClientId: 2,
+ Input: nil,
+ Call: 10,
+ Output: nil,
+ Return: 20,
+ },
+ },
+ expectError: "Overlapping operations, new.call(10) <= prev.return(100)",
+ },
+ }
+
+ for _, tc := range tcs {
+ h := NewAppendableHistory(identity.NewIDProvider())
+
+ for _, operation := range tc.operations[:len(tc.operations)-1] {
+ h.append(operation)
+ }
+ assert.PanicsWithValue(t, tc.expectError, func() { h.append(tc.operations[len(tc.operations)-1]) })
+ }
+}
diff --git a/tests/robustness/model/non_deterministic.go b/tests/robustness/model/non_deterministic.go
index 3167e340fd16..b9a28abe00b1 100644
--- a/tests/robustness/model/non_deterministic.go
+++ b/tests/robustness/model/non_deterministic.go
@@ -15,46 +15,153 @@
package model
import (
- "encoding/json"
+ "cmp"
"fmt"
- "reflect"
+ "slices"
+ "strings"
+ "sync/atomic"
"github.com/anishathalye/porcupine"
)
-// NonDeterministicModel extends DeterministicModel to allow for clients with imperfect knowledge of request destiny.
-// Unknown/error response doesn't inform whether request was persisted or not, so model
-// considers both cases. This is represented as multiple equally possible deterministic states.
+// LinearizationDeadlineTripped is set when linearization validation exceeds
+// its timeout.
+//
+// Porcupine currently does not propagate its timeout to the model Step
+// callback. A single non-deterministic Step can take seconds or minutes, so
+// CheckOperationsVerbose may not return promptly after its timeout expires.
+// Until etcd depends on a Porcupine version with timeout propagation, validation
+// uses this atomic flag to let Step and its helpers return early.
+//
+// See https://github.com/anishathalye/porcupine/pull/45 and https://github.com/etcd-io/etcd/pull/21715.
+var LinearizationDeadlineTripped atomic.Int32
+
+// NonDeterministicModel extends DeterministicModel to allow for clients with imperfect knowledge of the request's destiny.
+// An unknown/error response doesn't inform whether the request was persisted or not, so the model
+// considers both cases. This is represented as multiple, equally possible deterministic states.
// Failed requests fork the possible states, while successful requests merge and filter them.
-var NonDeterministicModel = porcupine.Model{
- Init: func() any {
- data, err := json.Marshal(nonDeterministicState{freshEtcdState()})
- if err != nil {
- panic(err)
- }
- return string(data)
- },
- Step: func(st any, in any, out any) (bool, any) {
- var states nonDeterministicState
- err := json.Unmarshal([]byte(st.(string)), &states)
- if err != nil {
- panic(err)
- }
- ok, states := states.apply(in.(EtcdRequest), out.(MaybeEtcdResponse))
- data, err := json.Marshal(states)
- if err != nil {
- panic(err)
- }
- return ok, string(data)
- },
- DescribeOperation: func(in, out any) string {
- return fmt.Sprintf("%s -> %s", describeEtcdRequest(in.(EtcdRequest)), describeEtcdResponse(in.(EtcdRequest), out.(MaybeEtcdResponse)))
- },
+var NonDeterministicModel = func(keys []string) porcupine.Model {
+ return porcupine.Model{
+ Init: func() any {
+ return nonDeterministicState{freshEtcdState(keys)}
+ },
+ Step: func(st any, in any, out any) (bool, any) {
+ return st.(nonDeterministicState).apply(in.(EtcdRequest), out.(MaybeEtcdResponse))
+ },
+ Equal: func(st1, st2 any) bool {
+ return st1.(nonDeterministicState).Equal(st2.(nonDeterministicState))
+ },
+ DescribeOperation: func(in, out any) string {
+ return fmt.Sprintf("%s -> %s", describeEtcdRequest(in.(EtcdRequest)), describeEtcdResponse(in.(EtcdRequest), out.(MaybeEtcdResponse)))
+ },
+ DescribeOperationMetadata: func(info any) string {
+ if info == nil {
+ return ""
+ }
+ return DescribeOperationMetadata(info.(MaybeEtcdResponse))
+ },
+ DescribeState: func(st any) string {
+ etcdStates := st.(nonDeterministicState)
+ desc := make([]string, 0, len(etcdStates))
+
+ slices.SortFunc(etcdStates, compareStates)
+
+ for i, s := range etcdStates {
+ // Describe just 3 first states before truncating
+ if i >= 3 {
+ desc = append(desc, "...truncated...")
+ break
+ }
+ desc = append(desc, describeEtcdState(s))
+ }
+
+ return strings.Join(desc, "\n")
+ },
+ }
}
type nonDeterministicState []EtcdState
+func (states nonDeterministicState) Equal(other nonDeterministicState) bool {
+ if len(states) != len(other) {
+ return false
+ }
+ slices.SortFunc(states, compareStates)
+ slices.SortFunc(other, compareStates)
+
+ for i := range states {
+ if !states[i].Equal(other[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func compareStates(first, second EtcdState) int {
+ if c := cmp.Compare(first.Revision, second.Revision); c != 0 {
+ return c
+ }
+ if c := cmp.Compare(first.CompactRevision, second.CompactRevision); c != 0 {
+ return c
+ }
+ if c := cmp.Compare(len(first.KeyValues), len(second.KeyValues)); c != 0 {
+ return c
+ }
+ for i := range first.KeyValues {
+ if (first.KeyValues[i] == nil) != (second.KeyValues[i] == nil) {
+ if first.KeyValues[i] == nil {
+ return -1
+ }
+ return 1
+ }
+ if first.KeyValues[i] == nil {
+ continue
+ }
+ if c := cmp.Compare(first.KeyValues[i].ModRevision, second.KeyValues[i].ModRevision); c != 0 {
+ return c
+ }
+ if c := cmp.Compare(first.KeyValues[i].Version, second.KeyValues[i].Version); c != 0 {
+ return c
+ }
+ if c := cmp.Compare(first.KeyValues[i].Value.Value, second.KeyValues[i].Value.Value); c != 0 {
+ return c
+ }
+ if c := cmp.Compare(first.KeyValues[i].Value.Hash, second.KeyValues[i].Value.Hash); c != 0 {
+ return c
+ }
+ }
+ if c := cmp.Compare(len(first.KeyLeases), len(second.KeyLeases)); c != 0 {
+ return c
+ }
+ for i := range first.KeyLeases {
+ if (first.KeyLeases[i] == nil) != (second.KeyLeases[i] == nil) {
+ if first.KeyLeases[i] == nil {
+ return -1
+ }
+ return 1
+ }
+ if first.KeyLeases[i] == nil {
+ continue
+ }
+ if c := cmp.Compare(*first.KeyLeases[i], *second.KeyLeases[i]); c != 0 {
+ return c
+ }
+ }
+ if c := cmp.Compare(len(first.Leases), len(second.Leases)); c != 0 {
+ return c
+ }
+ for i := range first.Leases {
+ if c := cmp.Compare(first.Leases[i], second.Leases[i]); c != 0 {
+ return c
+ }
+ }
+ return 0
+}
+
func (states nonDeterministicState) apply(request EtcdRequest, response MaybeEtcdResponse) (bool, nonDeterministicState) {
+ if LinearizationDeadlineTripped.Load() != 0 {
+ return false, nil
+ }
var newStates nonDeterministicState
switch {
case response.Error != "":
@@ -73,9 +180,12 @@ func (states nonDeterministicState) apply(request EtcdRequest, response MaybeEtc
func (states nonDeterministicState) applyFailedRequest(request EtcdRequest) nonDeterministicState {
newStates := make(nonDeterministicState, 0, len(states)*2)
for _, s := range states {
+ if LinearizationDeadlineTripped.Load() != 0 {
+ return nil
+ }
newStates = append(newStates, s)
newState, _ := s.Step(request)
- if !reflect.DeepEqual(newState, s) {
+ if !newState.Equal(s) {
newStates = append(newStates, newState)
}
}
@@ -86,6 +196,9 @@ func (states nonDeterministicState) applyFailedRequest(request EtcdRequest) nonD
func (states nonDeterministicState) applyPersistedRequest(request EtcdRequest) nonDeterministicState {
newStates := make(nonDeterministicState, 0, len(states))
for _, s := range states {
+ if LinearizationDeadlineTripped.Load() != 0 {
+ return nil
+ }
newState, _ := s.Step(request)
newStates = append(newStates, newState)
}
@@ -96,6 +209,9 @@ func (states nonDeterministicState) applyPersistedRequest(request EtcdRequest) n
func (states nonDeterministicState) applyPersistedRequestWithRevision(request EtcdRequest, responseRevision int64) nonDeterministicState {
newStates := make(nonDeterministicState, 0, len(states))
for _, s := range states {
+ if LinearizationDeadlineTripped.Load() != 0 {
+ return nil
+ }
newState, modelResponse := s.Step(request)
if modelResponse.Revision == responseRevision {
newStates = append(newStates, newState)
@@ -108,6 +224,9 @@ func (states nonDeterministicState) applyPersistedRequestWithRevision(request Et
func (states nonDeterministicState) applyRequestWithResponse(request EtcdRequest, response EtcdResponse) nonDeterministicState {
newStates := make(nonDeterministicState, 0, len(states))
for _, s := range states {
+ if LinearizationDeadlineTripped.Load() != 0 {
+ return nil
+ }
newState, modelResponse := s.Step(request)
if Match(modelResponse, MaybeEtcdResponse{EtcdResponse: response}) {
newStates = append(newStates, newState)
diff --git a/tests/robustness/model/non_deterministic_test.go b/tests/robustness/model/non_deterministic_test.go
index b6622e6fa812..1960f75e9c7c 100644
--- a/tests/robustness/model/non_deterministic_test.go
+++ b/tests/robustness/model/non_deterministic_test.go
@@ -15,13 +15,11 @@
package model
import (
- "encoding/json"
"errors"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/mvccpb"
)
@@ -328,15 +326,15 @@ func TestModelNonDeterministic(t *testing.T) {
for _, tc := range nonDeterministicTestScenarios {
tc := tc
t.Run(tc.name, func(t *testing.T) {
- state := NonDeterministicModel.Init()
+ keys := keysFromTestOperations(tc.operations)
+ model := NonDeterministicModel(keys)
+ state := model.Init()
for _, op := range tc.operations {
- ok, newState := NonDeterministicModel.Step(state, op.req, op.resp)
+ ok, newState := model.Step(state, op.req, op.resp)
if ok != !op.expectFailure {
t.Logf("state: %v", state)
- t.Errorf("Unexpected operation result, expect: %v, got: %v, operation: %s", !op.expectFailure, ok, NonDeterministicModel.DescribeOperation(op.req, op.resp))
- var loadedState nonDeterministicState
- err := json.Unmarshal([]byte(state.(string)), &loadedState)
- require.NoErrorf(t, err, "Failed to load state")
+ t.Errorf("Unexpected operation result, expect: %v, got: %v, operation: %s", !op.expectFailure, ok, model.DescribeOperation(op.req, op.resp))
+ loadedState := state.(nonDeterministicState)
for i, s := range loadedState {
_, resp := s.Step(op.req)
t.Errorf("For state %d, response diff: %s", i, cmp.Diff(op.resp, resp))
@@ -543,6 +541,11 @@ func TestModelResponseMatch(t *testing.T) {
resp2: MaybeEtcdResponse{Persisted: true, PersistedRevision: 3},
expectMatch: false,
},
+ {
+ resp1: putResponseWithMemberID(2, 1),
+ resp2: putResponseWithMemberID(2, 2),
+ expectMatch: true,
+ },
{
resp1: failedResponse(errors.New("failed request")),
resp2: MaybeEtcdResponse{Persisted: true},
diff --git a/tests/robustness/model/replay.go b/tests/robustness/model/replay.go
index 7f2be26e8ed2..a9f3653b1db5 100644
--- a/tests/robustness/model/replay.go
+++ b/tests/robustness/model/replay.go
@@ -16,13 +16,25 @@ package model
import (
"fmt"
- "sort"
+ "maps"
+ "slices"
"strings"
+
+ "github.com/anishathalye/porcupine"
)
+func NewReplayFromOperations(ops []porcupine.Operation) *EtcdReplay {
+ requests := []EtcdRequest{}
+ for _, op := range ops {
+ requests = append(requests, op.Input.(EtcdRequest))
+ }
+ return NewReplay(requests)
+}
+
func NewReplay(persistedRequests []EtcdRequest) *EtcdReplay {
- state := freshEtcdState()
- // Padding for index 0 and 1, so index matches revision..
+ keys := keysFromRequests(persistedRequests)
+ state := freshEtcdState(keys)
+ // Padding for index 0 and 1, so the index matches the revision.
revisionToEtcdState := []EtcdState{state, state}
var events []PersistedEvent
for _, request := range persistedRequests {
@@ -74,7 +86,7 @@ func toWatchEvents(prevState *EtcdState, request EtcdRequest, response MaybeEtcd
} else {
ops = request.Txn.OperationsOnSuccess
}
- for _, op := range ops {
+ for i, op := range ops {
switch op.Type {
case RangeOperation:
case DeleteOperation:
@@ -85,11 +97,11 @@ func toWatchEvents(prevState *EtcdState, request EtcdRequest, response MaybeEtcd
},
Revision: response.Revision,
}
- if _, ok := prevState.KeyValues[op.Delete.Key]; ok {
+ if response.Txn.Results[i].Deleted != 0 {
events = append(events, e)
}
case PutOperation:
- _, leaseExists := prevState.Leases[op.Put.LeaseID]
+ leaseExists := prevState.leaseExists(op.Put.LeaseID)
if op.Put.LeaseID != 0 && !leaseExists {
break
}
@@ -102,7 +114,7 @@ func toWatchEvents(prevState *EtcdState, request EtcdRequest, response MaybeEtcd
},
Revision: response.Revision,
}
- if _, ok := prevState.KeyValues[op.Put.Key]; !ok {
+ if _, ok := prevState.GetValue(op.Put.Key); !ok {
e.IsCreate = true
}
events = append(events, e)
@@ -111,15 +123,7 @@ func toWatchEvents(prevState *EtcdState, request EtcdRequest, response MaybeEtcd
}
}
case LeaseRevoke:
- deletedKeys := []string{}
- for key := range prevState.Leases[request.LeaseRevoke.LeaseID].Keys {
- if _, ok := prevState.KeyValues[key]; ok {
- deletedKeys = append(deletedKeys, key)
- }
- }
-
- sort.Strings(deletedKeys)
- for _, key := range deletedKeys {
+ for _, key := range prevState.leaseKeys(request.LeaseRevoke.LeaseID) {
e := PersistedEvent{
Event: Event{
Type: DeleteOperation,
@@ -134,20 +138,20 @@ func toWatchEvents(prevState *EtcdState, request EtcdRequest, response MaybeEtcd
}
type WatchEvent struct {
- PersistedEvent
- PrevValue *ValueRevision
+ PersistedEvent `json:",omitempty"`
+ PrevValue *ValueRevision `json:",omitempty"`
}
type PersistedEvent struct {
- Event
- Revision int64
- IsCreate bool
+ Event `json:",omitempty"`
+ Revision int64 `json:",omitempty"`
+ IsCreate bool `json:",omitempty"`
}
type Event struct {
- Type OperationType
- Key string
- Value ValueOrHash
+ Type OperationType `json:",omitempty"`
+ Key string `json:",omitempty"`
+ Value ValueOrHash `json:",omitempty"`
}
func (e Event) Match(request WatchRequest) bool {
@@ -164,3 +168,41 @@ type WatchRequest struct {
WithProgressNotify bool
WithPrevKV bool
}
+
+func ModelKeys(operations []porcupine.Operation) []string {
+ requests := []EtcdRequest{}
+ for _, op := range operations {
+ requests = append(requests, op.Input.(EtcdRequest))
+ }
+ return keysFromRequests(requests)
+}
+
+func keysFromRequests(requests []EtcdRequest) []string {
+ keysMap := map[string]bool{}
+ for _, request := range requests {
+ switch request.Type {
+ case Range:
+ keysMap[request.Range.Start] = true
+ if request.Range.End != "" {
+ keysMap[request.Range.End] = true
+ }
+ case Txn:
+ for _, op := range slices.Concat(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure) {
+ switch op.Type {
+ case RangeOperation:
+ keysMap[op.Range.Start] = true
+ if op.Range.End != "" {
+ keysMap[op.Range.End] = true
+ }
+ case PutOperation:
+ keysMap[op.Put.Key] = true
+ case DeleteOperation:
+ keysMap[op.Delete.Key] = true
+ }
+ }
+ }
+ }
+ keys := slices.Collect(maps.Keys(keysMap))
+ slices.Sort(keys)
+ return keys
+}
diff --git a/tests/robustness/model/types.go b/tests/robustness/model/types.go
new file mode 100644
index 000000000000..280a90464744
--- /dev/null
+++ b/tests/robustness/model/types.go
@@ -0,0 +1,251 @@
+// Copyright 2023 The etcd Authors
+//
+// 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 model
+
+import (
+ "encoding/json"
+ "errors"
+ "hash/fnv"
+ "reflect"
+ "slices"
+
+ "go.etcd.io/etcd/client/pkg/v3/types"
+)
+
+// RevisionForNonLinearizableResponse is a fake revision value used to
+// separate responses for requests that are not linearizable,
+// thus we need to ignore it in model when checking linearization.
+const RevisionForNonLinearizableResponse = -1
+
+type RequestType string
+
+const (
+ Range RequestType = "range"
+ Txn RequestType = "txn"
+ LeaseGrant RequestType = "leaseGrant"
+ LeaseRevoke RequestType = "leaseRevoke"
+ Defragment RequestType = "defragment"
+ Compact RequestType = "compact"
+)
+
+type EtcdRequest struct {
+ Type RequestType
+ LeaseGrant *LeaseGrantRequest
+ LeaseRevoke *LeaseRevokeRequest
+ Range *RangeRequest
+ Txn *TxnRequest
+ Defragment *DefragmentRequest
+ Compact *CompactRequest
+}
+
+func (r *EtcdRequest) IsRead() bool {
+ if r.Type == Range {
+ return true
+ }
+ if r.Type != Txn {
+ return false
+ }
+ for _, op := range append(r.Txn.OperationsOnSuccess, r.Txn.OperationsOnFailure...) {
+ if op.Type != RangeOperation {
+ return false
+ }
+ }
+ return true
+}
+
+type RangeRequest struct {
+ RangeOptions
+ Revision int64
+}
+
+type RangeOptions struct {
+ Start string
+ End string
+ Limit int64
+ KeysOnly bool
+}
+
+type PutOptions struct {
+ Key string
+ Value ValueOrHash
+ LeaseID int64
+}
+
+type DeleteOptions struct {
+ Key string
+}
+
+type TxnRequest struct {
+ Conditions []EtcdCondition
+ OperationsOnSuccess []EtcdOperation
+ OperationsOnFailure []EtcdOperation
+}
+
+func (txn *TxnRequest) AllOperations() []EtcdOperation {
+ return slices.Concat(txn.OperationsOnSuccess, txn.OperationsOnFailure)
+}
+
+type EtcdCondition struct {
+ Key string
+ ExpectedRevision int64
+ ExpectedVersion int64
+}
+
+type EtcdOperation struct {
+ Type OperationType
+ Range RangeOptions
+ Put PutOptions
+ Delete DeleteOptions
+}
+
+type OperationType string
+
+const (
+ RangeOperation OperationType = "range-operation"
+ PutOperation OperationType = "put-operation"
+ DeleteOperation OperationType = "delete-operation"
+)
+
+type LeaseGrantRequest struct {
+ LeaseID int64
+}
+type LeaseRevokeRequest struct {
+ LeaseID int64
+}
+type DefragmentRequest struct{}
+
+// MaybeEtcdResponse extends EtcdResponse to include partial information about responses to a request.
+// Possible response state information:
+// * Normal response. Client observed response. Only EtcdResponse is set.
+// * Persisted. Client didn't observe response, but we know it was persisted by etcd. Only Persisted is set
+// * Persisted with Revision. Client didn't observe a response, but we know that it was persisted, and its revision. Both Persisted and PersistedRevision is set.
+// * Error response. Client observed error, but we don't know if it was persisted. Only Error is set.
+type MaybeEtcdResponse struct {
+ EtcdResponse
+ Persisted bool
+ PersistedRevision int64
+ Error string
+}
+
+var ErrEtcdFutureRev = errors.New("future rev")
+
+type MemberID uint64
+
+// MarshalJSON implements the json.Marshaler interface for MemberID.
+// It marshals the MemberID as a hexadecimal string.
+func (m MemberID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(types.ID(m).String())
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface for MemberID.
+// It unmarshals the MemberID from a hexadecimal string.
+func (m *MemberID) UnmarshalJSON(data []byte) error {
+ var s string
+ if err := json.Unmarshal(data, &s); err != nil {
+ return err
+ }
+
+ id, err := types.IDFromString(s)
+ if err != nil {
+ return err
+ }
+ *m = MemberID(id)
+ return nil
+}
+
+type EtcdResponse struct {
+ Txn *TxnResponse
+ Range *RangeResponse
+ LeaseGrant *LeaseGrantResponse
+ LeaseRevoke *LeaseRevokeResponse
+ Defragment *DefragmentResponse
+ Compact *CompactResponse
+ ClientError string
+ Revision int64
+ MemberID MemberID `json:"member-id,omitempty"`
+}
+
+func Match(r1, r2 MaybeEtcdResponse) bool {
+ r1Revision := r1.Revision
+ if r1.Persisted {
+ r1Revision = r1.PersistedRevision
+ }
+ r2Revision := r2.Revision
+ if r2.Persisted {
+ r2Revision = r2.PersistedRevision
+ }
+
+ r1.EtcdResponse.MemberID = 0
+ r2.EtcdResponse.MemberID = 0
+
+ return (r1.Persisted && r1.PersistedRevision == 0) || (r2.Persisted && r2.PersistedRevision == 0) || ((r1.Persisted || r2.Persisted) && (r1.Error != "" || r2.Error != "" || r1Revision == r2Revision)) || reflect.DeepEqual(r1, r2)
+}
+
+type TxnResponse struct {
+ Failure bool
+ Results []EtcdOperationResult
+}
+
+type RangeResponse struct {
+ KVs []KeyValue
+ Count int64
+}
+
+type LeaseGrantResponse struct {
+ LeaseID int64
+}
+type (
+ LeaseRevokeResponse struct{}
+ DefragmentResponse struct{}
+)
+
+type EtcdOperationResult struct {
+ RangeResponse
+ Deleted int64
+}
+
+type KeyValue struct {
+ Key string
+ ValueRevision
+}
+
+type ValueRevision struct {
+ Value ValueOrHash `json:",omitempty"`
+ ModRevision int64 `json:",omitempty"`
+ Version int64 `json:",omitempty"`
+}
+
+type ValueOrHash struct {
+ Value string `json:",omitempty"`
+ Hash uint32 `json:",omitempty"`
+}
+
+func ToValueOrHash(value string) ValueOrHash {
+ v := ValueOrHash{}
+ if len(value) < 20 {
+ v.Value = value
+ } else {
+ h := fnv.New32a()
+ h.Write([]byte(value))
+ v.Hash = h.Sum32()
+ }
+ return v
+}
+
+type CompactResponse struct{}
+
+type CompactRequest struct {
+ Revision int64
+}
diff --git a/tests/robustness/model/types_test.go b/tests/robustness/model/types_test.go
new file mode 100644
index 000000000000..c0cd72d2781d
--- /dev/null
+++ b/tests/robustness/model/types_test.go
@@ -0,0 +1,54 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 model
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "go.etcd.io/etcd/client/pkg/v3/types"
+)
+
+func TestMemberIDMatchesTypesID(t *testing.T) {
+ raw := uint64(13770331908272521436)
+
+ mID := MemberID(raw)
+ jsonBytes, err := json.Marshal(mID)
+ require.NoError(t, err)
+ jsonStr := string(jsonBytes)
+
+ tID := types.ID(raw)
+ expectedHex := tID.String()
+ require.JSONEq(t, "\""+expectedHex+"\"", jsonStr)
+}
+
+func TestMemberIDMatchesTypesIDUnmarshal(t *testing.T) {
+ raw := uint64(13770331908272521999)
+ mID := MemberID(raw)
+
+ jsonBytes, err := json.Marshal(mID)
+ require.NoError(t, err)
+
+ tID := types.ID(raw)
+ expectedHex := "\"" + tID.String() + "\""
+ require.JSONEq(t, expectedHex, string(jsonBytes))
+
+ var parsedID MemberID
+ err = json.Unmarshal(jsonBytes, &parsedID)
+ require.NoError(t, err)
+ require.Equal(t, mID, parsedID)
+}
diff --git a/tests/robustness/model/watch.go b/tests/robustness/model/watch.go
index fc880e30ede6..de2c54d1cc44 100644
--- a/tests/robustness/model/watch.go
+++ b/tests/robustness/model/watch.go
@@ -17,14 +17,14 @@ package model
import "time"
type WatchOperation struct {
- Request WatchRequest
- Responses []WatchResponse
+ Request WatchRequest `json:",omitempty"`
+ Responses []WatchResponse `json:",omitempty"`
}
type WatchResponse struct {
- Events []WatchEvent
- IsProgressNotify bool
- Revision int64
- Time time.Duration
- Error string
+ Events []WatchEvent `json:",omitempty"`
+ IsProgressNotify bool `json:",omitempty"`
+ Revision int64 `json:",omitempty"`
+ Time time.Duration `json:",omitempty"`
+ Error string `json:",omitempty"`
}
diff --git a/tests/robustness/options/cluster_options.go b/tests/robustness/options/cluster_options.go
index 01031e218469..455d78322416 100644
--- a/tests/robustness/options/cluster_options.go
+++ b/tests/robustness/options/cluster_options.go
@@ -25,8 +25,8 @@ var internalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
type ClusterOptions []e2e.EPClusterOption
-// WithClusterOptionGroups takes an array of EPClusterOption arrays, and randomly picks one EPClusterOption array when constructing the config.
-// This function is mainly used to group strongly coupled config options together, so that we can dynamically test different groups of options.
+// WithClusterOptionGroups takes an array of EPClusterOption arrays and randomly picks one EPClusterOption array when constructing the config.
+// This function is mainly used to group strongly coupled config options together so that we can dynamically test different groups of options.
func WithClusterOptionGroups(input ...ClusterOptions) e2e.EPClusterOption {
return func(c *e2e.EtcdProcessClusterConfig) {
optsPicked := input[internalRand.Intn(len(input))]
@@ -36,10 +36,10 @@ func WithClusterOptionGroups(input ...ClusterOptions) e2e.EPClusterOption {
}
}
-// WithSubsetOptions randomly select a subset of input options, and apply the subset to the cluster config.
+// WithSubsetOptions randomly selects a subset of input options and applies the subset to the cluster config.
func WithSubsetOptions(input ...e2e.EPClusterOption) e2e.EPClusterOption {
return func(c *e2e.EtcdProcessClusterConfig) {
- // selects random subsetLen (0 to len(input)) elements from the input array.
+ // selects a random subsetLen (0 to len(input)) elements from the input array.
subsetLen := internalRand.Intn(len(input) + 1)
perm := internalRand.Perm(len(input))
for i := 0; i < subsetLen; i++ {
diff --git a/tests/robustness/options/server_config_options.go b/tests/robustness/options/server_config_options.go
index ade51592cd1c..a0a6b5d5a875 100644
--- a/tests/robustness/options/server_config_options.go
+++ b/tests/robustness/options/server_config_options.go
@@ -26,9 +26,9 @@ func WithSnapshotCount(input ...uint64) e2e.EPClusterOption {
}
}
-func WithExperimentalCompactionBatchLimit(input ...int) e2e.EPClusterOption {
+func WithCompactionBatchLimit(input ...int) e2e.EPClusterOption {
return func(c *e2e.EtcdProcessClusterConfig) {
- c.ServerConfig.ExperimentalCompactionBatchLimit = input[internalRand.Intn(len(input))]
+ c.ServerConfig.CompactionBatchLimit = input[internalRand.Intn(len(input))]
}
}
@@ -50,9 +50,9 @@ func WithElectionMs(input ...uint) e2e.EPClusterOption {
}
}
-func WithExperimentalWatchProgressNotifyInterval(input ...time.Duration) e2e.EPClusterOption {
+func WithWatchProgressNotifyInterval(input ...time.Duration) e2e.EPClusterOption {
return func(c *e2e.EtcdProcessClusterConfig) {
- c.ServerConfig.ExperimentalWatchProgressNotifyInterval = input[internalRand.Intn(len(input))]
+ c.ServerConfig.WatchProgressNotifyInterval = input[internalRand.Intn(len(input))]
}
}
diff --git a/tests/robustness/report/client.go b/tests/robustness/report/client.go
index 48d29b8ae818..4585f91ec013 100644
--- a/tests/robustness/report/client.go
+++ b/tests/robustness/report/client.go
@@ -22,10 +22,8 @@ import (
"sort"
"strconv"
"strings"
- "testing"
"github.com/anishathalye/porcupine"
- "github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.etcd.io/etcd/tests/v3/robustness/model"
@@ -47,25 +45,32 @@ func (r ClientReport) WatchEventCount() int {
return count
}
-func persistClientReports(t *testing.T, lg *zap.Logger, path string, reports []ClientReport) {
+func persistClientReports(lg *zap.Logger, path string, reports []ClientReport) error {
sort.Slice(reports, func(i, j int) bool {
return reports[i].ClientID < reports[j].ClientID
})
for _, r := range reports {
clientDir := filepath.Join(path, fmt.Sprintf("client-%d", r.ClientID))
- err := os.MkdirAll(clientDir, 0o700)
- require.NoError(t, err)
+ if err := os.MkdirAll(clientDir, 0o700); err != nil {
+ return err
+ }
+
if len(r.Watch) != 0 {
- persistWatchOperations(t, lg, filepath.Join(clientDir, "watch.json"), r.Watch)
+ if err := persistWatchOperations(lg, filepath.Join(clientDir, "watch.json"), r.Watch); err != nil {
+ return err
+ }
} else {
lg.Info("no watch operations for client, skip persisting", zap.Int("client-id", r.ClientID))
}
if len(r.KeyValue) != 0 {
- persistKeyValueOperations(t, lg, filepath.Join(clientDir, "operations.json"), r.KeyValue)
+ if err := persistKeyValueOperations(lg, filepath.Join(clientDir, "operations.json"), r.KeyValue); err != nil {
+ return err
+ }
} else {
lg.Info("no KV operations for client, skip persisting", zap.Int("client-id", r.ClientID))
}
}
+ return nil
}
func LoadClientReports(path string) ([]ClientReport, error) {
@@ -138,11 +143,11 @@ func loadKeyValueOperations(path string) (operations []porcupine.Operation, err
if os.IsNotExist(err) {
return nil, nil
}
- return nil, fmt.Errorf("failed to open watch operation file: %q, err: %w", path, err)
+ return nil, fmt.Errorf("failed to open KV operation file: %q, err: %w", path, err)
}
file, err := os.OpenFile(path, os.O_RDONLY, 0o755)
if err != nil {
- return nil, fmt.Errorf("failed to open watch operation file: %q, err: %w", path, err)
+ return nil, fmt.Errorf("failed to open KV operation file: %q, err: %w", path, err)
}
defer file.Close()
decoder := json.NewDecoder(file)
@@ -156,7 +161,7 @@ func loadKeyValueOperations(path string) (operations []porcupine.Operation, err
}
err = decoder.Decode(&operation)
if err != nil {
- return nil, fmt.Errorf("failed to decode watch operation, err: %w", err)
+ return nil, fmt.Errorf("failed to decode KV operation, err: %w", err)
}
operations = append(operations, porcupine.Operation{
ClientId: operation.ClientID,
@@ -164,41 +169,57 @@ func loadKeyValueOperations(path string) (operations []porcupine.Operation, err
Call: operation.Call,
Output: operation.Output,
Return: operation.Return,
+ Metadata: operation.Output,
})
}
return operations, nil
}
-func persistWatchOperations(t *testing.T, lg *zap.Logger, path string, responses []model.WatchOperation) {
+func persistWatchOperations(lg *zap.Logger, path string, responses []model.WatchOperation) error {
lg.Info("Saving watch operations", zap.String("path", path))
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
- t.Errorf("Failed to save watch operations: %v", err)
- return
+ return fmt.Errorf("failed to save watch operations: %w", err)
}
defer file.Close()
- encoder := json.NewEncoder(file)
for _, resp := range responses {
- err := encoder.Encode(resp)
+ data, err := json.MarshalIndent(resp, "", " ")
if err != nil {
- t.Errorf("Failed to encode operation: %v", err)
+ return fmt.Errorf("failed to encode operation: %w", err)
}
+ file.Write(data)
+ file.WriteString("\n")
}
+ return nil
}
-func persistKeyValueOperations(t *testing.T, lg *zap.Logger, path string, operations []porcupine.Operation) {
+func persistKeyValueOperations(lg *zap.Logger, path string, operations []porcupine.Operation) error {
lg.Info("Saving operation history", zap.String("path", path))
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
- t.Errorf("Failed to save operation history: %v", err)
- return
+ return fmt.Errorf("Failed to save KV operation history: %w", err)
}
defer file.Close()
- encoder := json.NewEncoder(file)
for _, op := range operations {
- err := encoder.Encode(op)
+ data, err := json.MarshalIndent(op, "", " ")
if err != nil {
- t.Errorf("Failed to encode operation: %v", err)
+ return fmt.Errorf("Failed to encode KV operation: %w", err)
+ }
+ file.Write(data)
+ file.WriteString("\n")
+ }
+ return nil
+}
+
+func OperationsMaxRevision(reports []ClientReport) int64 {
+ var maxRevision int64
+ for _, r := range reports {
+ for _, op := range r.KeyValue {
+ resp := op.Output.(model.MaybeEtcdResponse)
+ if resp.Revision > maxRevision {
+ maxRevision = resp.Revision
+ }
}
}
+ return maxRevision
}
diff --git a/tests/robustness/report/client_test.go b/tests/robustness/report/client_test.go
index 07da049c74ea..3e00dc990436 100644
--- a/tests/robustness/report/client_test.go
+++ b/tests/robustness/report/client_test.go
@@ -38,7 +38,7 @@ func TestPersistLoadClientReports(t *testing.T) {
start := time.Since(baseTime)
time.Sleep(time.Nanosecond)
stop := time.Since(baseTime)
- h.AppendRange("key", "", 0, 0, start, stop, &clientv3.GetResponse{Header: &etcdserverpb.ResponseHeader{Revision: 2}, Count: 2, Kvs: []*mvccpb.KeyValue{{
+ h.AppendRange("key", "", 0, 0, false, start, stop, &clientv3.GetResponse{Header: &etcdserverpb.ResponseHeader{Revision: 2}, Count: 2, Kvs: []*mvccpb.KeyValue{{
Key: []byte("key"),
ModRevision: 2,
Value: []byte("value"),
@@ -133,7 +133,8 @@ func TestPersistLoadClientReports(t *testing.T) {
},
}
path := t.TempDir()
- persistClientReports(t, zaptest.NewLogger(t), path, reports)
+ err := persistClientReports(zaptest.NewLogger(t), path, reports)
+ require.NoError(t, err)
got, err := LoadClientReports(path)
require.NoError(t, err)
if diff := cmp.Diff(reports, got, cmpopts.EquateEmpty()); diff != "" {
diff --git a/tests/robustness/report/report.go b/tests/robustness/report/report.go
index b3d3d5a5e32c..d1bfbc87b914 100644
--- a/tests/robustness/report/report.go
+++ b/tests/robustness/report/report.go
@@ -15,71 +15,107 @@
package report
import (
+ "encoding/json"
"fmt"
"os"
+ "path"
"path/filepath"
- "strings"
- "testing"
- "time"
- "github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.etcd.io/etcd/tests/v3/framework/e2e"
)
type TestReport struct {
- Logger *zap.Logger
- Cluster *e2e.EtcdProcessCluster
- Client []ClientReport
- Visualize func(path string) error
+ logger *zap.Logger
+ reportPath string
+ serverDataPaths map[string]string
+ clientReports []ClientReport
+ visualize func(lg *zap.Logger, path string) error
+ traffic *TrafficDetail
+ dataSaved bool
}
-func testResultsDirectory(t *testing.T) string {
- resultsDirectory, ok := os.LookupEnv("RESULTS_DIR")
- if !ok {
- resultsDirectory = "/tmp/"
+func NewTestReport(lg *zap.Logger, reportPath string, serverDataPaths map[string]string, traffic *TrafficDetail) (*TestReport, error) {
+ if reportPath == "" {
+ return nil, fmt.Errorf("reportPath is not set")
}
- resultsDirectory, err := filepath.Abs(resultsDirectory)
- if err != nil {
- panic(err)
- }
- path, err := filepath.Abs(filepath.Join(
- resultsDirectory, strings.ReplaceAll(t.Name(), "/", "_"), fmt.Sprintf("%v", time.Now().UnixNano())))
- require.NoError(t, err)
- err = os.RemoveAll(path)
- require.NoError(t, err)
- err = os.MkdirAll(path, 0o700)
- require.NoError(t, err)
- return path
+ return &TestReport{
+ logger: lg,
+ reportPath: reportPath,
+ serverDataPaths: serverDataPaths,
+ traffic: traffic,
+ }, nil
+}
+
+func (r *TestReport) SetClientReports(reports []ClientReport) {
+ r.clientReports = reports
}
-func (r *TestReport) Report(t *testing.T, force bool) {
- _, persistResultsEnvSet := os.LookupEnv("PERSIST_RESULTS")
- if !t.Failed() && !force && !persistResultsEnvSet {
- return
+func (r *TestReport) SetVisualizer(visualize func(lg *zap.Logger, path string) error) {
+ r.visualize = visualize
+}
+
+func (r *TestReport) SaveEtcdData() error {
+ if r.dataSaved {
+ return nil
+ }
+ r.logger.Info("Saving etcd data", zap.String("path", r.reportPath))
+ err := os.RemoveAll(r.reportPath)
+ if err != nil {
+ r.logger.Error("Failed to remove report dir", zap.Error(err))
}
- path := testResultsDirectory(t)
- r.Logger.Info("Saving robustness test report", zap.String("path", path))
- for _, member := range r.Cluster.Procs {
- memberDataDir := filepath.Join(path, fmt.Sprintf("server-%s", member.Config().Name))
- persistMemberDataDir(t, r.Logger, member, memberDataDir)
+ for server, dataPath := range r.serverDataPaths {
+ serverReportPath := filepath.Join(r.reportPath, fmt.Sprintf("server-%s", server))
+ r.logger.Info("Saving member data dir", zap.String("member", server), zap.String("data-dir", dataPath), zap.String("path", serverReportPath))
+ if err := os.CopyFS(serverReportPath, os.DirFS(dataPath)); err != nil {
+ return err
+ }
}
- if r.Client != nil {
- persistClientReports(t, r.Logger, path, r.Client)
+ if r.clientReports != nil {
+ if err := persistClientReports(r.logger, r.reportPath, r.clientReports); err != nil {
+ return err
+ }
}
- if r.Visualize != nil {
- err := r.Visualize(filepath.Join(path, "history.html"))
- if err != nil {
- t.Error(err)
+ if r.traffic != nil {
+ if err := persistTrafficDetail(r.logger, r.reportPath, *r.traffic); err != nil {
+ return err
}
}
+ r.dataSaved = true
+ return nil
}
-func persistMemberDataDir(t *testing.T, lg *zap.Logger, member e2e.EtcdProcess, path string) {
- lg.Info("Saving member data dir", zap.String("member", member.Config().Name), zap.String("path", path))
- err := os.Rename(memberDataDir(member), path)
- require.NoError(t, err)
+func (r *TestReport) Finalize(tFailed bool, panicked bool) error {
+ _, persistResults := os.LookupEnv("PERSIST_RESULTS")
+ keep := tFailed || panicked || persistResults
+ if !keep {
+ if !r.dataSaved {
+ return nil
+ }
+ r.logger.Info("Removing robustness test report", zap.String("path", r.reportPath))
+ return os.RemoveAll(r.reportPath)
+ }
+
+ if err := r.SaveEtcdData(); err != nil {
+ return fmt.Errorf("failed to save etcd data: %w", err)
+ }
+
+ if r.visualize == nil {
+ r.logger.Info("No visualization available to be saved", zap.String("path", r.reportPath))
+ return nil
+ }
+ r.logger.Info("Adding visualization to test report", zap.String("path", r.reportPath))
+ return r.visualize(r.logger, filepath.Join(r.reportPath, "history.html"))
+}
+
+func ServerDataPaths(c *e2e.EtcdProcessCluster) map[string]string {
+ dataPaths := make(map[string]string)
+ for _, member := range c.Procs {
+ dataPaths[member.Config().Name] = memberDataDir(member)
+ }
+
+ return dataPaths
}
func memberDataDir(member e2e.EtcdProcess) string {
@@ -89,3 +125,28 @@ func memberDataDir(member e2e.EtcdProcess) string {
}
return member.Config().DataDirPath
}
+
+type TrafficDetail struct {
+ ExpectUniqueRevision bool `json:"expectuniquerevision,omitempty"`
+}
+
+const trafficDetailFileName = "traffic.json"
+
+func persistTrafficDetail(lg *zap.Logger, p string, td TrafficDetail) error {
+ lg.Info("Saving traffic configuration details", zap.String("path", path.Join(p, trafficDetailFileName)))
+ b, err := json.Marshal(td)
+ if err != nil {
+ return nil
+ }
+ return os.WriteFile(filepath.Join(p, trafficDetailFileName), b, 0o644)
+}
+
+func LoadTrafficDetail(p string) (TrafficDetail, error) {
+ var detail TrafficDetail
+ b, err := os.ReadFile(filepath.Join(p, trafficDetailFileName))
+ if err != nil {
+ return TrafficDetail{}, err
+ }
+ err = json.Unmarshal(b, &detail)
+ return detail, err
+}
diff --git a/tests/robustness/report/wal.go b/tests/robustness/report/wal.go
index 077ddc8896aa..6abde5d44ede 100644
--- a/tests/robustness/report/wal.go
+++ b/tests/robustness/report/wal.go
@@ -15,18 +15,22 @@
package report
import (
+ "bytes"
"errors"
"fmt"
"io"
+ "math"
"os"
"path/filepath"
"strings"
"github.com/google/go-cmp/cmp"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/testing/protocmp"
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
- "go.etcd.io/etcd/pkg/v3/pbutil"
+ "go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/server/v3/storage/datadir"
"go.etcd.io/etcd/server/v3/storage/wal"
"go.etcd.io/etcd/server/v3/storage/wal/walpb"
@@ -46,7 +50,7 @@ func LoadClusterPersistedRequests(lg *zap.Logger, path string) ([]model.EtcdRequ
dataDirs = append(dataDirs, filepath.Join(path, file.Name()))
}
}
- return PersistedRequestsDirs(lg, dataDirs)
+ return PersistedRequests(lg, dataDirs)
}
func PersistedRequestsCluster(lg *zap.Logger, cluster *e2e.EtcdProcessCluster) ([]model.EtcdRequest, error) {
@@ -54,70 +58,157 @@ func PersistedRequestsCluster(lg *zap.Logger, cluster *e2e.EtcdProcessCluster) (
for _, proc := range cluster.Procs {
dataDirs = append(dataDirs, memberDataDir(proc))
}
- return PersistedRequestsDirs(lg, dataDirs)
+ return PersistedRequests(lg, dataDirs)
}
-func PersistedRequestsDirs(lg *zap.Logger, dataDirs []string) ([]model.EtcdRequest, error) {
- persistedRequests := []model.EtcdRequest{}
- // Allow failure in minority of etcd cluster.
- // 0 failures in 1 node cluster, 1 failure in 3 node cluster
- allowedFailures := len(dataDirs) / 2
- for _, dir := range dataDirs {
- memberRequests, err := requestsPersistedInWAL(lg, dir)
+func PersistedRequests(lg *zap.Logger, dataDirs []string) ([]model.EtcdRequest, error) {
+ if len(dataDirs) == 0 {
+ return nil, errors.New("no data dirs")
+ }
+ entriesPersistedInWAL := make([][]*raftpb.Entry, len(dataDirs))
+ var minCommitIndex uint64 = math.MaxUint64
+ for i, dir := range dataDirs {
+ state, entries, err := ReadWAL(lg, dir)
if err != nil {
- if allowedFailures < 1 {
- return nil, err
- }
- allowedFailures--
+ lg.Error("Failed to read WAL", zap.Error(err), zap.String("data-dir", dir))
continue
}
- minLength := min(len(persistedRequests), len(memberRequests))
- if diff := cmp.Diff(memberRequests[:minLength], persistedRequests[:minLength]); diff != "" {
- return nil, fmt.Errorf("unexpected differences between wal entries, diff:\n%s", diff)
- }
- if len(memberRequests) > len(persistedRequests) {
- persistedRequests = memberRequests
- }
+ minCommitIndex = min(minCommitIndex, state.GetCommit())
+ entriesPersistedInWAL[i] = entries
}
- return persistedRequests, nil
-}
-
-func requestsPersistedInWAL(lg *zap.Logger, dataDir string) ([]model.EtcdRequest, error) {
- _, ents, err := ReadWAL(lg, dataDir)
+ entries, err := mergeMembersEntries(minCommitIndex, entriesPersistedInWAL)
if err != nil {
return nil, err
}
- requests := make([]model.EtcdRequest, 0, len(ents))
- for _, ent := range ents {
- if ent.Type != raftpb.EntryNormal || len(ent.Data) == 0 {
+ persistedRequests := make([]model.EtcdRequest, 0, len(entries))
+ for _, e := range entries {
+ if e.GetType() != raftpb.EntryNormal {
continue
}
- request, err := parseEntryNormal(ent)
+ request, err := parseEntryNormal(e)
if err != nil {
return nil, err
}
if request != nil {
- requests = append(requests, *request)
+ persistedRequests = append(persistedRequests, *request)
}
}
- return requests, nil
+ return persistedRequests, nil
}
-func ReadWAL(lg *zap.Logger, dataDir string) (state raftpb.HardState, ents []raftpb.Entry, err error) {
+func mergeMembersEntries(minCommitIndex uint64, memberEntries [][]*raftpb.Entry) ([]*raftpb.Entry, error) {
+ for _, entries := range memberEntries {
+ var lastIndex uint64
+ for _, e := range entries {
+ if e.GetIndex() <= lastIndex {
+ return nil, fmt.Errorf("raft index should increase, got: %d, previous: %d", e.GetIndex(), lastIndex)
+ }
+ lastIndex = e.GetIndex()
+ }
+ }
+ memberIndices := make([]int, len(memberEntries))
+ mergedHistory := []*raftpb.Entry{}
+ var raftIndex uint64
+ for {
+ // Find entry with raftIndex.
+ raftIndex++
+ entriesLeft := false
+ for i, entries := range memberEntries {
+ memberIndex := memberIndices[i]
+ for memberIndex < len(entries) && entries[memberIndex].GetIndex() < raftIndex {
+ memberIndex++
+ }
+ if memberIndex < len(entries) {
+ entriesLeft = true
+ }
+ memberIndices[i] = memberIndex
+ }
+ if !entriesLeft {
+ break
+ }
+ // Entries collects votes from matching entries.
+ votes := make([]int, len(memberEntries))
+ for i := 0; i < len(memberEntries); i++ {
+ if len(memberEntries[i]) <= memberIndices[i] {
+ continue
+ }
+ entry1 := memberEntries[i][memberIndices[i]]
+ if entry1.GetIndex() != raftIndex {
+ continue
+ }
+ for j := i; j < len(memberEntries); j++ {
+ if i == j {
+ votes[i]++
+ continue
+ }
+ if len(memberEntries[j]) <= memberIndices[j] {
+ continue
+ }
+ entry2 := memberEntries[j][memberIndices[j]]
+ if entry2.GetIndex() != raftIndex {
+ continue
+ }
+ if areEntriesEqual(entry1, entry2) {
+ votes[i]++
+ votes[j]++
+ }
+ }
+ }
+ // Select entry with most votes
+ topVotes := 0
+ for _, vote := range votes {
+ if vote > topVotes {
+ topVotes = vote
+ }
+ }
+ if topVotes == 0 {
+ return nil, fmt.Errorf("no entry for raft index %d", raftIndex)
+ }
+ var entryWithMostVotes *raftpb.Entry
+ for i, vote := range votes {
+ if vote != topVotes {
+ continue
+ }
+ entry := memberEntries[i][memberIndices[i]]
+ if entryWithMostVotes == nil {
+ entryWithMostVotes = entry
+ continue
+ }
+ if entryWithMostVotes.GetTerm() != entry.GetTerm() && entry.GetIndex() > minCommitIndex {
+ if entryWithMostVotes.GetTerm() < entry.GetTerm() {
+ entryWithMostVotes = entry
+ }
+ continue
+ }
+ if !areEntriesEqual(entryWithMostVotes, entry) {
+ diff := cmp.Diff(entryWithMostVotes, entry, protocmp.Transform(), protocmp.IgnoreDefaultScalars(), cmp.Comparer(bytes.Equal))
+ return nil, fmt.Errorf("mismatching entries on raft index %d, diff: %s", raftIndex, diff)
+ }
+ }
+ mergedHistory = append(mergedHistory, proto.Clone(entryWithMostVotes).(*raftpb.Entry))
+ }
+ if len(mergedHistory) == 0 {
+ return nil, errors.New("no WAL entries matched")
+ }
+ return mergedHistory, nil
+}
+
+func areEntriesEqual(e1, e2 *raftpb.Entry) bool {
+ if e1 == nil || e2 == nil {
+ return e1 == e2
+ }
+ return e1.GetIndex() == e2.GetIndex() &&
+ e1.GetTerm() == e2.GetTerm() &&
+ e1.GetType() == e2.GetType() &&
+ bytes.Equal(e1.GetData(), e2.GetData())
+}
+
+func ReadWAL(lg *zap.Logger, dataDir string) (state *raftpb.HardState, ents []*raftpb.Entry, err error) {
walDir := datadir.ToWALDir(dataDir)
repaired := false
for {
- w, err := wal.OpenForRead(lg, walDir, walpb.Snapshot{Index: 0})
- if err != nil {
- return state, nil, fmt.Errorf("failed to open WAL, err: %w", err)
- }
- _, state, ents, err = w.ReadAll()
- w.Close()
+ state, ents, err = ReadAllWALEntries(lg, walDir)
if err != nil {
- if errors.Is(err, wal.ErrSnapshotNotFound) || errors.Is(err, wal.ErrSliceOutOfRange) {
- lg.Info("Error occurred when reading WAL entries", zap.Error(err))
- return state, ents, nil
- }
// we can only repair ErrUnexpectedEOF and we never repair twice.
if repaired || !errors.Is(err, io.ErrUnexpectedEOF) {
return state, nil, fmt.Errorf("failed to read WAL, cannot be repaired, err: %w", err)
@@ -133,16 +224,24 @@ func ReadWAL(lg *zap.Logger, dataDir string) (state raftpb.HardState, ents []raf
}
}
-func parseEntryNormal(ent raftpb.Entry) (*model.EtcdRequest, error) {
+func parseEntryNormal(ent *raftpb.Entry) (*model.EtcdRequest, error) {
var raftReq pb.InternalRaftRequest
- if err := raftReq.Unmarshal(ent.Data); err != nil {
- var r pb.Request
- isV2Entry := pbutil.MaybeUnmarshal(&r, ent.Data)
- if !isV2Entry {
- return nil, err
- }
+ if len(ent.Data) == 0 {
return nil, nil
}
+ if err := proto.Unmarshal(ent.Data, &raftReq); err != nil {
+ // PR https://github.com/etcd-io/etcd/pull/21263 removed v2 requests
+ // in etcd v3.7. However, robustness always uses the latest protobuf
+ // definitions to parse WAL entries generated by all previous versions.
+ // etcd v3.4 and v3.5 generate v2 requests during bootstrap, which the
+ // v3.7 protobuf can no longer parse. As a result, robustness fails when
+ // parsing those WAL entries. We intentionally ignore this error here.
+ // See https://github.com/etcd-io/etcd/pull/21263#discussion_r2776042340
+ if strings.Contains(err.Error(), "proto: wrong wireType") {
+ return nil, nil
+ }
+ return nil, err
+ }
switch {
case raftReq.Put != nil:
op := model.PutOptions{
@@ -226,7 +325,7 @@ func parseEntryNormal(ent raftpb.Entry) (*model.EtcdRequest, error) {
}
return &request, nil
default:
- panic(fmt.Sprintf("Unhandled raft request: %+v", raftReq))
+ panic(fmt.Sprintf("Unhandled raft request: %s", raftReq.String()))
}
}
@@ -237,9 +336,10 @@ func toEtcdOperation(op *pb.RequestOp) (operation model.EtcdOperation) {
operation = model.EtcdOperation{
Type: model.RangeOperation,
Range: model.RangeOptions{
- Start: string(rangeOp.Key),
- End: string(rangeOp.RangeEnd),
- Limit: rangeOp.Limit,
+ Start: string(rangeOp.Key),
+ End: string(rangeOp.RangeEnd),
+ Limit: rangeOp.Limit,
+ KeysOnly: rangeOp.GetKeysOnly(),
},
}
case op.GetRequestPut() != nil:
@@ -264,3 +364,56 @@ func toEtcdOperation(op *pb.RequestOp) (operation model.EtcdOperation) {
}
return operation
}
+
+func ReadAllWALEntries(lg *zap.Logger, dirpath string) (state *raftpb.HardState, ents []*raftpb.Entry, err error) {
+ names, err := fileutil.ReadDir(dirpath)
+ if err != nil {
+ return state, nil, err
+ }
+ files := make([]fileutil.FileReader, 0, len(names))
+ for _, name := range names {
+ if !strings.HasSuffix(name, ".wal") {
+ continue
+ }
+ p := filepath.Join(dirpath, name)
+ var f *os.File
+ f, err = os.OpenFile(p, os.O_RDONLY, fileutil.PrivateFileMode)
+ if err != nil {
+ return state, nil, fmt.Errorf("os.OpenFile failed (%q): %w", p, err)
+ }
+ defer f.Close()
+ files = append(files, fileutil.NewFileReader(f))
+ }
+ rec := &walpb.Record{}
+ decoder := wal.NewDecoder(files...)
+ for err = decoder.Decode(rec); err == nil; err = decoder.Decode(rec) {
+ switch rec.GetType() {
+ case wal.EntryType:
+ e := wal.MustUnmarshalEntry(rec.Data)
+ i := len(ents)
+ for ; i > 0 && ents[i-1].GetIndex() >= e.GetIndex(); i-- {
+ }
+ // The line below is potentially overriding some 'uncommitted' entries.
+ ents = append(ents[:i], e)
+ case wal.StateType:
+ state = wal.MustUnmarshalState(rec.Data)
+ case wal.MetadataType:
+ case wal.CrcType:
+ crc := decoder.LastCRC()
+ // current crc of decoder must match the crc of the record.
+ // do no need to match 0 crc, since the decoder is a new one at this case.
+ if crc != 0 && rec.Validate(crc) != nil {
+ state.Reset()
+ return state, nil, wal.ErrCRCMismatch
+ }
+ decoder.UpdateCRC(rec.GetCrc())
+ case wal.SnapshotType:
+ default:
+ return state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
+ }
+ }
+ if err != nil && !errors.Is(err, io.EOF) {
+ return state, nil, err
+ }
+ return state, ents, nil
+}
diff --git a/tests/robustness/report/wal_test.go b/tests/robustness/report/wal_test.go
new file mode 100644
index 000000000000..9e27644e1ed0
--- /dev/null
+++ b/tests/robustness/report/wal_test.go
@@ -0,0 +1,748 @@
+// Copyright 2025 The etcd Authors
+//
+// 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.
+
+//nolint:govet
+package report
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/zap/zaptest"
+ "google.golang.org/protobuf/proto"
+
+ "go.etcd.io/etcd/server/v3/storage/wal"
+ "go.etcd.io/etcd/server/v3/storage/wal/walpb"
+ "go.etcd.io/raft/v3/raftpb"
+)
+
+func TestMergeMemberEntries(t *testing.T) {
+ tcs := []struct {
+ name string
+ minCommitIndex uint64
+ memberEntries [][]*raftpb.Entry
+ expectErr string
+ expectEntries []*raftpb.Entry
+ }{
+ {
+ name: "Error when empty data dir",
+ memberEntries: [][]*raftpb.Entry{},
+ expectErr: "no WAL entries matched",
+ },
+ {
+ name: "Success when no entries",
+ memberEntries: [][]*raftpb.Entry{
+ {},
+ },
+ expectErr: "no WAL entries matched",
+ },
+ {
+ name: "Error when one member cluster didn't observe the index",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectErr: "no entry for raft index 2",
+ },
+ {
+ name: "Error when entries index unordered",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ },
+ },
+ expectErr: "raft index should increase, got: 1, previous: 3",
+ },
+ {
+ name: "Error when entries index duplicated",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ },
+ },
+ expectErr: "raft index should increase, got: 1, previous: 1",
+ },
+ {
+ name: "Success when one member cluster",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success when three members agree on entries",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success when three members have no entries",
+ memberEntries: [][]*raftpb.Entry{
+ {}, {}, {},
+ },
+ expectErr: "no WAL entries matched",
+ },
+ {
+ name: "Success when one member has no entries in three node cluster",
+ memberEntries: [][]*raftpb.Entry{
+ {},
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success if two members have no entries in three node cluster",
+ memberEntries: [][]*raftpb.Entry{
+ {},
+ {},
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success if members didn't observe the whole history",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success if members observed only one part of history",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Error when in three member cluster if no members observed index",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectErr: "no entry for raft index 2",
+ },
+ {
+ name: "Success if only one member observed history",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {},
+ {},
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Success when one member observed different last entry",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("x")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Data: []byte("b")},
+ {Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ {
+ name: "Error when one member didn't observe whole history and others observed different last entry",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("x")},
+ },
+ },
+ expectErr: "mismatching entries on raft index 3",
+ },
+ {
+ name: "Error when three members observed different last entry",
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("x")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("y")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("z")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectErr: "mismatching entries on raft index 2",
+ },
+ {
+ name: "Error when one member observed empty history and others differ on last entry",
+ memberEntries: [][]*raftpb.Entry{
+ {},
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("x")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Data: []byte("y")},
+ &raftpb.Entry{Index: new(uint64(2)), Data: []byte("b")},
+ &raftpb.Entry{Index: new(uint64(3)), Data: []byte("c")},
+ },
+ },
+ expectErr: "mismatching entries on raft index 1",
+ },
+ {
+ name: "Error if entries mismatch on index before minCommitIndex",
+ minCommitIndex: 2,
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Term: new(uint64(1)), Data: []byte("b")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Term: new(uint64(2)), Data: []byte("c")},
+ },
+ },
+ expectErr: "mismatching entries on raft index 2",
+ },
+ {
+ name: "Select entry with higher term if they conflict on uncommitted index",
+ minCommitIndex: 1,
+ memberEntries: [][]*raftpb.Entry{
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Term: new(uint64(1)), Data: []byte("b")},
+ },
+ {
+ &raftpb.Entry{Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte("a")},
+ &raftpb.Entry{Index: new(uint64(2)), Term: new(uint64(2)), Data: []byte("x")},
+ },
+ },
+ expectEntries: []*raftpb.Entry{
+ {Index: new(uint64(1)), Term: new(uint64(1)), Data: []byte("a")},
+ {Index: new(uint64(2)), Term: new(uint64(2)), Data: []byte("x")},
+ },
+ },
+ }
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ entries, err := mergeMembersEntries(tc.minCommitIndex, tc.memberEntries)
+ if tc.expectErr == "" {
+ require.NoError(t, err)
+ } else {
+ require.ErrorContains(t, err, tc.expectErr)
+ }
+ if diff := cmp.Diff(tc.expectEntries, entries, cmpopts.IgnoreUnexported(raftpb.Entry{}, raftpb.HardState{})); diff != "" {
+ t.Errorf("expectEntries mismatch (-want +got):\n%s", diff)
+ }
+ })
+ }
+}
+
+func TestWriteReadWAL(t *testing.T) {
+ type batch struct {
+ state *raftpb.HardState
+ entries []*raftpb.Entry
+ snapshot *walpb.Snapshot
+ }
+ type want struct {
+ wantState raftpb.HardState
+ wantEntries []*raftpb.Entry
+ wantError string
+ }
+
+ tcs := []struct {
+ name string
+ operations []batch
+ readAt *walpb.Snapshot
+ walReadAll want
+ readAllEntries want
+ }{
+ {
+ name: "single batch",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(5))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ {
+ name: "multiple committed batches",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(2))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(4))},
+ entries: []*raftpb.Entry{{Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(5))},
+ entries: []*raftpb.Entry{{Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ {
+ name: "uncommitted ovewritten entries",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(1))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("a")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(3))},
+ entries: []*raftpb.Entry{{Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("b")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(4))},
+ entries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("c")}, {Index: new(uint64(5)), Data: []byte("c")}},
+ },
+ },
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(4))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("c")}, {Index: new(uint64(5)), Data: []byte("c")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(4))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("c")}, {Index: new(uint64(5)), Data: []byte("c")}},
+ },
+ },
+ {
+ name: "entries in bad order",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(2))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(6))},
+ entries: []*raftpb.Entry{{Index: new(uint64(5)), Data: []byte("e")}, {Index: new(uint64(6)), Data: []byte("f")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(4))},
+ entries: []*raftpb.Entry{{Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}},
+ },
+ },
+ walReadAll: want{
+ wantError: "slice bounds out of range",
+ wantState: raftpb.HardState{Commit: new(uint64(2))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(4))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}},
+ },
+ },
+ {
+ name: "read before snapshot",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(1))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ snapshot: &walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(0)), ConfState: &raftpb.ConfState{}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(5))},
+ entries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ walReadAll: want{
+ wantError: "slice bounds out of range",
+ wantState: raftpb.HardState{Commit: new(uint64(1))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ {
+ name: "read at snapshot",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(1))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ snapshot: &walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(0)), ConfState: &raftpb.ConfState{}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(5))},
+ entries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ readAt: &walpb.Snapshot{Index: new(uint64(3))},
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ {
+ name: "uncommitted entries before snapshot",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(1))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(3))},
+ entries: []*raftpb.Entry{{Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}},
+ },
+ {
+ snapshot: &walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(0)), ConfState: &raftpb.ConfState{}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(4))},
+ entries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("e")}, {Index: new(uint64(5)), Data: []byte("f")}},
+ },
+ },
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(4))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("e")}, {Index: new(uint64(5)), Data: []byte("f")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(4))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("e")}, {Index: new(uint64(5)), Data: []byte("f")}},
+ },
+ },
+ {
+ name: "entries preceding snapshot",
+ operations: []batch{
+ {
+ snapshot: &walpb.Snapshot{Index: new(uint64(4)), Term: new(uint64(0)), ConfState: &raftpb.ConfState{}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(2))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(4))},
+ entries: []*raftpb.Entry{{Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(6))},
+ entries: []*raftpb.Entry{{Index: new(uint64(5)), Data: []byte("e")}, {Index: new(uint64(6)), Data: []byte("f")}},
+ },
+ },
+ readAt: &walpb.Snapshot{Index: new(uint64(4))},
+ walReadAll: want{
+ wantState: raftpb.HardState{Commit: new(uint64(6))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(5)), Data: []byte("e")}, {Index: new(uint64(6)), Data: []byte("f")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(6))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(3)), Data: []byte("c")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}, {Index: new(uint64(6)), Data: []byte("f")}},
+ },
+ },
+ {
+ name: "read after snapshot",
+ operations: []batch{
+ {
+ state: &raftpb.HardState{Commit: new(uint64(1))},
+ entries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}},
+ },
+ {
+ snapshot: &walpb.Snapshot{Index: new(uint64(3)), Term: new(uint64(0)), ConfState: &raftpb.ConfState{}},
+ },
+ {
+ state: &raftpb.HardState{Commit: new(uint64(5))},
+ entries: []*raftpb.Entry{{Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ readAt: &walpb.Snapshot{Index: new(uint64(4))},
+ walReadAll: want{
+ wantError: "snapshot not found",
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ readAllEntries: want{
+ wantState: raftpb.HardState{Commit: new(uint64(5))},
+ wantEntries: []*raftpb.Entry{{Index: new(uint64(1)), Data: []byte("a")}, {Index: new(uint64(2)), Data: []byte("b")}, {Index: new(uint64(4)), Data: []byte("d")}, {Index: new(uint64(5)), Data: []byte("e")}},
+ },
+ },
+ }
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ dir := t.TempDir()
+ lg := zaptest.NewLogger(t)
+ w, err := wal.Create(lg, dir, nil)
+ require.NoError(t, err)
+ for _, op := range tc.operations {
+ if op.state != nil {
+ err = w.Save(op.state, op.entries)
+ require.NoError(t, err)
+ }
+ if op.snapshot != nil {
+ err = w.SaveSnapshot(op.snapshot)
+ require.NoError(t, err)
+ }
+ }
+ w.Close()
+
+ w2, err := wal.OpenForRead(lg, dir, tc.readAt)
+ require.NoError(t, err)
+ defer w2.Close()
+ t.Run("wal.ReadAll", func(t *testing.T) {
+ _, state, entries, err := w2.ReadAll()
+ if tc.walReadAll.wantError != "" {
+ require.ErrorContains(t, err, tc.walReadAll.wantError)
+ } else {
+ require.NoError(t, err)
+ }
+ if !proto.Equal(&tc.walReadAll.wantState, state) {
+ t.Errorf("wantState mismatch\n got %+v\n want %+v", state, tc.walReadAll.wantState)
+ }
+ if diff := cmp.Diff(tc.walReadAll.wantEntries, entries, cmpopts.IgnoreUnexported(raftpb.Entry{}, raftpb.HardState{})); diff != "" {
+ t.Errorf("wantEntries mismatch (-want +got):\n%s", diff)
+ }
+ })
+ t.Run("ReadAllEntries", func(t *testing.T) {
+ state, entries, err := ReadAllWALEntries(lg, dir)
+ if tc.readAllEntries.wantError != "" {
+ require.ErrorContains(t, err, tc.walReadAll.wantError)
+ } else {
+ require.NoError(t, err)
+ }
+ if !proto.Equal(&tc.readAllEntries.wantState, state) {
+ t.Errorf("wantState mismatch\n got %+v\n want %+v", state, tc.readAllEntries.wantState)
+ }
+ if diff := cmp.Diff(tc.readAllEntries.wantEntries, entries, cmpopts.IgnoreUnexported(raftpb.Entry{}, raftpb.HardState{})); diff != "" {
+ t.Errorf("wantEntries mismatch (-want +got):\n%s", diff)
+ }
+ })
+ })
+ }
+}
+
+func TestAreEntriesEqualVerifyAllFields(t *testing.T) {
+ base := &raftpb.Entry{
+ Index: new(uint64(1)),
+ Term: new(uint64(1)),
+ Type: raftpb.EntryNormal.Enum(),
+ Data: []byte("data"),
+ }
+
+ val := reflect.ValueOf(base).Elem()
+ typ := val.Type()
+
+ for i := 0; i < val.NumField(); i++ {
+ field := typ.Field(i)
+ if !field.IsExported() {
+ continue
+ }
+
+ t.Run(field.Name, func(t *testing.T) {
+ modified := proto.Clone(base).(*raftpb.Entry)
+ modVal := reflect.ValueOf(modified).Elem()
+
+ f := modVal.Field(i)
+ switch f.Kind() {
+ case reflect.Pointer:
+ if f.Type().Elem().Kind() == reflect.Uint64 {
+ v := uint64(999)
+ f.Set(reflect.ValueOf(&v))
+ } else if f.Type().Elem().Kind() == reflect.Int32 {
+ v := int32(999)
+ f.Set(reflect.ValueOf(&v).Convert(f.Type()))
+ }
+ case reflect.Slice:
+ if f.Type().Elem().Kind() == reflect.Uint8 {
+ f.SetBytes([]byte("different-data-payload"))
+ }
+ default:
+ t.Fatalf("Unhandled field type for reflection: %s", f.Type())
+ }
+
+ if areEntriesEqual(base, modified) {
+ t.Errorf("areEntriesEqual returned true after mutating field %q! This means changes to %q are not being compared.", field.Name, field.Name)
+ }
+ })
+ }
+}
+
+func BenchmarkMergeMemberEntries(b *testing.B) {
+ const numEntries = 1000
+ memberEntries := make([][]*raftpb.Entry, 3)
+ for i := 0; i < 3; i++ {
+ memberEntries[i] = make([]*raftpb.Entry, numEntries)
+ for j := 0; j < numEntries; j++ {
+ var entryType *raftpb.EntryType
+ if i == 0 {
+ entryType = nil
+ } else {
+ entryType = raftpb.EntryNormal.Enum()
+ }
+ memberEntries[i][j] = &raftpb.Entry{
+ Index: new(uint64(j + 1)),
+ Term: new(uint64(1)),
+ Type: entryType,
+ Data: []byte("some realistic data payload for entry"),
+ }
+ }
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := mergeMembersEntries(0, memberEntries)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/tests/robustness/scenarios/scenarios.go b/tests/robustness/scenarios/scenarios.go
index ba33add20c2e..64f4ab3e2497 100644
--- a/tests/robustness/scenarios/scenarios.go
+++ b/tests/robustness/scenarios/scenarios.go
@@ -15,6 +15,7 @@
package scenarios
import (
+ "os"
"path/filepath"
"testing"
"time"
@@ -25,7 +26,6 @@ import (
"go.etcd.io/etcd/client/pkg/v3/fileutil"
"go.etcd.io/etcd/server/v3/etcdserver"
"go.etcd.io/etcd/tests/v3/framework/e2e"
- "go.etcd.io/etcd/tests/v3/robustness/client"
"go.etcd.io/etcd/tests/v3/robustness/failpoint"
"go.etcd.io/etcd/tests/v3/robustness/options"
"go.etcd.io/etcd/tests/v3/robustness/random"
@@ -42,22 +42,38 @@ var trafficProfiles = []TrafficProfile{
{
Name: "EtcdHighTraffic",
Traffic: traffic.EtcdPut,
- Profile: traffic.HighTrafficProfile,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ },
},
{
Name: "EtcdTrafficDeleteLeases",
Traffic: traffic.EtcdPutDeleteLease,
- Profile: traffic.LowTraffic,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ },
},
{
Name: "KubernetesHighTraffic",
Traffic: traffic.Kubernetes,
- Profile: traffic.HighTrafficProfile,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ },
},
{
Name: "KubernetesLowTraffic",
Traffic: traffic.Kubernetes,
- Profile: traffic.LowTraffic,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ },
},
}
@@ -67,7 +83,6 @@ type TestScenario struct {
Cluster e2e.EtcdProcessClusterConfig
Traffic traffic.Traffic
Profile traffic.Profile
- Watch client.WatchConfig
}
func Exploratory(_ *testing.T) []TestScenario {
@@ -96,9 +111,13 @@ func Exploratory(_ *testing.T) []TestScenario {
options.WithSnapshotCount(50, 100, 1000),
options.WithSubsetOptions(randomizableOptions...),
e2e.WithGoFailEnabled(true),
- // Set low minimal compaction batch limit to allow for triggering multi batch compaction failpoints.
- options.WithExperimentalCompactionBatchLimit(10, 100, 1000),
- e2e.WithExperimentalWatchProcessNotifyInterval(100 * time.Millisecond),
+ // Set a low minimal compaction batch limit to allow for triggering multi batch compaction failpoints.
+ options.WithCompactionBatchLimit(10, 100, 1000),
+ e2e.WithWatchProcessNotifyInterval(100 * time.Millisecond),
+ }
+
+ if addr := os.Getenv("TRACING_SERVER_ADDR"); addr != "" {
+ baseOptions = append(baseOptions, e2e.WithEnableDistributedTracing(addr))
}
if e2e.CouldSetSnapshotCatchupEntries(e2e.BinPath.Etcd) {
@@ -135,17 +154,18 @@ func Exploratory(_ *testing.T) []TestScenario {
if e2e.BinPath.LazyFSAvailable() {
newScenarios := scenarios
for _, s := range scenarios {
- // LazyFS increases the load on CPU, so we run it with more lightweight case.
- if s.Profile.MinimalQPS <= 100 && s.Cluster.ClusterSize == 1 {
+ // LazyFS increases the load on the CPU, so we run it with a more lightweight case.
+ if s.Profile.KeyValue.MinimalQPS <= 100 && s.Cluster.ClusterSize == 1 {
lazyfsCluster := s.Cluster
lazyfsCluster.LazyFSEnabled = true
+ profileWithoutCompaction := s.Profile
+ profileWithoutCompaction.Compaction = nil
newScenarios = append(newScenarios, TestScenario{
Name: filepath.Join(s.Name, "LazyFS"),
Failpoint: s.Failpoint,
Cluster: lazyfsCluster,
Traffic: s.Traffic,
- Profile: s.Profile.WithoutCompaction(),
- Watch: s.Watch,
+ Profile: profileWithoutCompaction,
})
}
}
@@ -162,8 +182,11 @@ func Regression(t *testing.T) []TestScenario {
scenarios = append(scenarios, TestScenario{
Name: "Issue14370",
Failpoint: failpoint.RaftBeforeSavePanic,
- Profile: traffic.LowTraffic,
- Traffic: traffic.EtcdPutDeleteLease,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Compaction: &traffic.CompactionDefault,
+ },
+ Traffic: traffic.EtcdPutDeleteLease,
Cluster: *e2e.NewConfig(
e2e.WithClusterSize(1),
e2e.WithGoFailEnabled(true),
@@ -172,8 +195,11 @@ func Regression(t *testing.T) []TestScenario {
scenarios = append(scenarios, TestScenario{
Name: "Issue14685",
Failpoint: failpoint.DefragBeforeCopyPanic,
- Profile: traffic.LowTraffic,
- Traffic: traffic.EtcdPutDeleteLease,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Compaction: &traffic.CompactionDefault,
+ },
+ Traffic: traffic.EtcdPutDeleteLease,
Cluster: *e2e.NewConfig(
e2e.WithClusterSize(1),
e2e.WithGoFailEnabled(true),
@@ -182,18 +208,22 @@ func Regression(t *testing.T) []TestScenario {
scenarios = append(scenarios, TestScenario{
Name: "Issue13766",
Failpoint: failpoint.KillFailpoint,
- Profile: traffic.HighTrafficProfile,
- Traffic: traffic.EtcdPut,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Compaction: &traffic.CompactionDefault,
+ },
+ Traffic: traffic.EtcdPut,
Cluster: *e2e.NewConfig(
e2e.WithSnapshotCount(100),
),
})
scenarios = append(scenarios, TestScenario{
Name: "Issue15220",
- Watch: client.WatchConfig{
- RequestProgress: true,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
},
- Profile: traffic.LowTraffic,
Traffic: traffic.EtcdPutDeleteLease,
Failpoint: failpoint.KillFailpoint,
Cluster: *e2e.NewConfig(
@@ -201,8 +231,12 @@ func Regression(t *testing.T) []TestScenario {
),
})
scenarios = append(scenarios, TestScenario{
- Name: "Issue17529",
- Profile: traffic.HighTrafficProfile,
+ Name: "Issue17529",
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Watch: &traffic.WatchDefault,
+ Compaction: &traffic.CompactionDefault,
+ },
Traffic: traffic.Kubernetes,
Failpoint: failpoint.SleepBeforeSendWatchResponse,
Cluster: *e2e.NewConfig(
@@ -213,13 +247,15 @@ func Regression(t *testing.T) []TestScenario {
})
scenarios = append(scenarios, TestScenario{
- Name: "Issue17780",
- Profile: traffic.LowTraffic.WithoutCompaction(),
+ Name: "Issue17780",
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ },
Failpoint: failpoint.BatchCompactBeforeSetFinishedCompactPanic,
Traffic: traffic.Kubernetes,
Cluster: *e2e.NewConfig(
e2e.WithClusterSize(1),
- e2e.WithExperimentalCompactionBatchLimit(300),
+ e2e.WithCompactionBatchLimit(300),
e2e.WithSnapshotCount(1000),
e2e.WithGoFailEnabled(true),
),
@@ -240,24 +276,25 @@ func Regression(t *testing.T) []TestScenario {
scenarios = append(scenarios, TestScenario{
Name: "Issue19179",
Profile: traffic.Profile{
- MinimalQPS: 50,
- MaximalQPS: 100,
- BurstableQPS: 100,
- ClientCount: 8,
- MaxNonUniqueRequestConcurrency: 3,
- }.WithoutCompaction(),
+ KeyValue: &traffic.KeyValueVeryLow,
+ Watch: &traffic.WatchDefault,
+ },
Failpoint: failpoint.BatchCompactBeforeSetFinishedCompactPanic,
Traffic: traffic.KubernetesCreateDelete,
Cluster: *e2e.NewConfig(
e2e.WithClusterSize(1),
- e2e.WithExperimentalCompactionBatchLimit(50),
+ e2e.WithCompactionBatchLimit(50),
e2e.WithSnapshotCount(1000),
e2e.WithGoFailEnabled(true),
),
})
scenarios = append(scenarios, TestScenario{
- Name: "Issue18089",
- Profile: traffic.LowTraffic.WithCompactionPeriod(100 * time.Millisecond), // Use frequent compaction for high reproduce rate
+ Name: "Issue18089",
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueMedium,
+ Compaction: &traffic.CompactionFrequent, // Use frequent compaction for high reproduce rate
+ Watch: &traffic.WatchDefault,
+ },
Failpoint: failpoint.SleepBeforeSendWatchResponse,
Traffic: traffic.EtcdDelete,
Cluster: *e2e.NewConfig(
@@ -265,6 +302,26 @@ func Regression(t *testing.T) []TestScenario {
e2e.WithGoFailEnabled(true),
),
})
+ scenarios = append(scenarios, TestScenario{
+ Name: "Issue20221",
+ Failpoint: failpoint.BlackholeUntilSnapshot,
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Watch: &traffic.Watch{
+ MemberClientCount: 6,
+ ClusterClientCount: 2,
+ RevisionOffsetRange: traffic.Range{Min: 50, Max: 100},
+ },
+ },
+ Traffic: traffic.EtcdPut,
+ Cluster: *e2e.NewConfig(
+ e2e.WithSnapshotCount(10),
+ e2e.WithPeerProxy(true),
+ e2e.WithIsPeerTLS(true),
+ e2e.WithWatchProcessNotifyInterval(10*time.Millisecond),
+ e2e.WithSnapshotCatchUpEntries(10),
+ ),
+ })
if v.Compare(version.V3_5) >= 0 {
opts := []e2e.EPClusterOption{
e2e.WithSnapshotCount(100),
@@ -277,9 +334,13 @@ func Regression(t *testing.T) []TestScenario {
scenarios = append(scenarios, TestScenario{
Name: "Issue15271",
Failpoint: failpoint.BlackholeUntilSnapshot,
- Profile: traffic.HighTrafficProfile,
- Traffic: traffic.EtcdPut,
- Cluster: *e2e.NewConfig(opts...),
+ Profile: traffic.Profile{
+ KeyValue: &traffic.KeyValueHigh,
+ Compaction: &traffic.CompactionDefault,
+ Watch: &traffic.WatchDefault,
+ },
+ Traffic: traffic.EtcdPut,
+ Cluster: *e2e.NewConfig(opts...),
})
}
return scenarios
diff --git a/tests/robustness/traffic/etcd.go b/tests/robustness/traffic/etcd.go
index 906f3d2cb465..a34c072f2ac8 100644
--- a/tests/robustness/traffic/etcd.go
+++ b/tests/robustness/traffic/etcd.go
@@ -32,43 +32,46 @@ import (
var (
EtcdPutDeleteLease Traffic = etcdTraffic{
- keyCount: 10,
- leaseTTL: DefaultLeaseTTL,
- largePutSize: 32769,
+ keyCount: 10,
+ leaseTTL: DefaultLeaseTTL,
// Please keep the sum of weights equal 100.
requests: []random.ChoiceWeight[etcdRequestType]{
{Choice: Get, Weight: 15},
- {Choice: List, Weight: 15},
+ {Choice: List, Weight: 4},
+ {Choice: ListKeyOnly, Weight: 4},
+ {Choice: ListStream, Weight: 4},
+ {Choice: ListStreamKeyOnly, Weight: 3},
{Choice: StaleGet, Weight: 10},
- {Choice: StaleList, Weight: 10},
+ {Choice: StaleList, Weight: 5},
+ {Choice: StaleListStream, Weight: 5},
{Choice: Delete, Weight: 5},
- {Choice: MultiOpTxn, Weight: 5},
+ {Choice: MultiOpTxn, Weight: 10},
{Choice: PutWithLease, Weight: 5},
{Choice: LeaseRevoke, Weight: 5},
{Choice: CompareAndSet, Weight: 5},
{Choice: Put, Weight: 20},
- {Choice: LargePut, Weight: 5},
},
}
EtcdPut Traffic = etcdTraffic{
- keyCount: 10,
- largePutSize: 32769,
- leaseTTL: DefaultLeaseTTL,
+ keyCount: 10,
+ leaseTTL: DefaultLeaseTTL,
// Please keep the sum of weights equal 100.
requests: []random.ChoiceWeight[etcdRequestType]{
{Choice: Get, Weight: 15},
- {Choice: List, Weight: 15},
+ {Choice: List, Weight: 4},
+ {Choice: ListKeyOnly, Weight: 4},
+ {Choice: ListStream, Weight: 4},
+ {Choice: ListStreamKeyOnly, Weight: 3},
{Choice: StaleGet, Weight: 10},
- {Choice: StaleList, Weight: 10},
- {Choice: MultiOpTxn, Weight: 5},
- {Choice: LargePut, Weight: 5},
+ {Choice: StaleList, Weight: 5},
+ {Choice: StaleListStream, Weight: 5},
+ {Choice: MultiOpTxn, Weight: 10},
{Choice: Put, Weight: 40},
},
}
EtcdDelete Traffic = etcdTraffic{
- keyCount: 10,
- largePutSize: 32769,
- leaseTTL: DefaultLeaseTTL,
+ keyCount: 10,
+ leaseTTL: DefaultLeaseTTL,
// Please keep the sum of weights equal 100.
requests: []random.ChoiceWeight[etcdRequestType]{
{Choice: Put, Weight: 50},
@@ -78,10 +81,9 @@ var (
)
type etcdTraffic struct {
- keyCount int
- requests []random.ChoiceWeight[etcdRequestType]
- leaseTTL int64
- largePutSize int
+ keyCount int
+ requests []random.ChoiceWeight[etcdRequestType]
+ leaseTTL int64
}
func (t etcdTraffic) ExpectUniqueRevision() bool {
@@ -91,41 +93,44 @@ func (t etcdTraffic) ExpectUniqueRevision() bool {
type etcdRequestType string
const (
- Get etcdRequestType = "get"
- StaleGet etcdRequestType = "staleGet"
- List etcdRequestType = "list"
- StaleList etcdRequestType = "staleList"
- Put etcdRequestType = "put"
- LargePut etcdRequestType = "largePut"
- Delete etcdRequestType = "delete"
- MultiOpTxn etcdRequestType = "multiOpTxn"
- PutWithLease etcdRequestType = "putWithLease"
- LeaseRevoke etcdRequestType = "leaseRevoke"
- CompareAndSet etcdRequestType = "compareAndSet"
- Defragment etcdRequestType = "defragment"
+ Get etcdRequestType = "get"
+ StaleGet etcdRequestType = "staleGet"
+ List etcdRequestType = "list"
+ ListKeyOnly etcdRequestType = "listKeyOnly"
+ StaleList etcdRequestType = "staleList"
+ ListStream etcdRequestType = "listStream"
+ ListStreamKeyOnly etcdRequestType = "listStreamKeyOnly"
+ StaleListStream etcdRequestType = "staleListStream"
+ Put etcdRequestType = "put"
+ Delete etcdRequestType = "delete"
+ MultiOpTxn etcdRequestType = "multiOpTxn"
+ PutWithLease etcdRequestType = "putWithLease"
+ LeaseRevoke etcdRequestType = "leaseRevoke"
+ CompareAndSet etcdRequestType = "compareAndSet"
+ Defragment etcdRequestType = "defragment"
)
func (t etcdTraffic) Name() string {
return "Etcd"
}
-func (t etcdTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
+func (t etcdTraffic) RunKeyValueLoop(ctx context.Context, c *client.RecordingClient, p RunTrafficLoopParam) {
lastOperationSucceeded := true
var lastRev int64
var requestType etcdRequestType
client := etcdTrafficClient{
etcdTraffic: t,
- keyPrefix: "key",
+ keyStore: p.KeyStore,
client: c,
- limiter: limiter,
- idProvider: ids,
- leaseStorage: lm,
+ limiter: p.QPSLimiter,
+ idProvider: p.IDs,
+ leaseStorage: p.LeaseIDStorage,
}
for {
select {
case <-ctx.Done():
return
- case <-finish:
+ case <-p.Finish:
return
default:
}
@@ -134,16 +139,16 @@ func (t etcdTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClie
// Avoid multiple failed writes in a row
if lastOperationSucceeded {
choices := t.requests
- if shouldReturn = nonUniqueWriteLimiter.Take(); !shouldReturn {
+ if shouldReturn = p.NonUniqueRequestConcurrencyLimiter.Take(); !shouldReturn {
choices = filterOutNonUniqueEtcdWrites(choices)
}
requestType = random.PickRandom(choices)
} else {
- requestType = Get
+ requestType = List
}
rev, err := client.Request(ctx, requestType, lastRev)
if shouldReturn {
- nonUniqueWriteLimiter.Return()
+ p.NonUniqueRequestConcurrencyLimiter.Return()
}
lastOperationSucceeded = err == nil
if err != nil {
@@ -152,19 +157,25 @@ func (t etcdTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClie
if rev != 0 {
lastRev = rev
}
- limiter.Wait(ctx)
+ p.QPSLimiter.Wait(ctx)
}
}
-func (t etcdTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{}) {
+func (t etcdTraffic) RunWatchLoop(ctx context.Context, c *client.RecordingClient, p RunWatchLoopParam) {
+ runWatchLoop(ctx, c, p, watchLoopConfig{
+ key: p.KeyStore.GetPrefix(),
+ })
+}
+
+func (t etcdTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, param RunCompactLoopParam) {
var lastRev int64 = 2
- ticker := time.NewTicker(period)
+ ticker := time.NewTicker(param.Period)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
- case <-finish:
+ case <-param.Finish:
return
case <-ticker.C:
}
@@ -196,7 +207,7 @@ func filterOutNonUniqueEtcdWrites(choices []random.ChoiceWeight[etcdRequestType]
type etcdTrafficClient struct {
etcdTraffic
- keyPrefix string
+ keyStore *keyStore
client *client.RecordingClient
limiter *rate.Limiter
idProvider identity.Provider
@@ -211,57 +222,79 @@ func (c etcdTrafficClient) Request(ctx context.Context, request etcdRequestType,
switch request {
case StaleGet:
var resp *clientv3.GetResponse
- resp, err = c.client.Get(opCtx, c.randomKey(), clientv3.WithRev(lastRev))
+ resp, err = c.client.Get(opCtx, c.keyStore.GetKey(), clientv3.WithRev(lastRev))
if err == nil {
rev = resp.Header.Revision
}
case Get:
var resp *clientv3.GetResponse
- resp, err = c.client.Get(opCtx, c.randomKey(), clientv3.WithRev(0))
+ resp, err = c.client.Get(opCtx, c.keyStore.GetKey(), clientv3.WithRev(0))
if err == nil {
rev = resp.Header.Revision
}
case List:
var resp *clientv3.GetResponse
- resp, err = c.client.Range(ctx, c.keyPrefix, clientv3.GetPrefixRangeEnd(c.keyPrefix), 0, limit)
+ resp, err = c.client.Range(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), 0, limit, false)
+ if resp != nil {
+ c.keyStore.SyncKeys(resp)
+ rev = resp.Header.Revision
+ }
+ case ListKeyOnly:
+ var resp *clientv3.GetResponse
+ resp, err = c.client.Range(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), 0, limit, true)
if resp != nil {
+ c.keyStore.SyncKeys(resp)
rev = resp.Header.Revision
}
case StaleList:
var resp *clientv3.GetResponse
- resp, err = c.client.Range(ctx, c.keyPrefix, clientv3.GetPrefixRangeEnd(c.keyPrefix), lastRev, limit)
+ resp, err = c.client.Range(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), lastRev, limit, false)
if resp != nil {
rev = resp.Header.Revision
}
- case Put:
- var resp *clientv3.PutResponse
- resp, err = c.client.Put(opCtx, c.randomKey(), fmt.Sprintf("%d", c.idProvider.NewRequestID()))
+ case ListStream:
+ var resp *clientv3.GetResponse
+ resp, err = c.client.RangeStream(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), 0, limit, false)
if resp != nil {
+ c.keyStore.SyncKeys(resp)
rev = resp.Header.Revision
}
- case LargePut:
+ case ListStreamKeyOnly:
+ var resp *clientv3.GetResponse
+ resp, err = c.client.RangeStream(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), 0, limit, true)
+ if resp != nil {
+ c.keyStore.SyncKeys(resp)
+ rev = resp.Header.Revision
+ }
+ case StaleListStream:
+ var resp *clientv3.GetResponse
+ resp, err = c.client.RangeStream(opCtx, c.keyStore.GetPrefix(), clientv3.GetPrefixRangeEnd(c.keyStore.GetPrefix()), lastRev, limit, false)
+ if resp != nil {
+ rev = resp.Header.Revision
+ }
+ case Put:
var resp *clientv3.PutResponse
- resp, err = c.client.Put(opCtx, c.randomKey(), random.RandString(c.largePutSize))
+ resp, err = c.client.Put(opCtx, c.keyStore.GetKey(), fmt.Sprintf("%d", c.idProvider.NewRequestID()))
if resp != nil {
rev = resp.Header.Revision
}
case Delete:
var resp *clientv3.DeleteResponse
- resp, err = c.client.Delete(opCtx, c.randomKey())
+ resp, err = c.client.Delete(opCtx, c.keyStore.GetKeyForDelete())
if resp != nil {
rev = resp.Header.Revision
}
case MultiOpTxn:
var resp *clientv3.TxnResponse
resp, err = c.client.Txn(opCtx).Then(
- c.pickMultiTxnOps()...,
+ c.pickMultiTxnOps(c.keyStore)...,
).Commit()
if resp != nil {
rev = resp.Header.Revision
}
case CompareAndSet:
var kv *mvccpb.KeyValue
- key := c.randomKey()
+ key := c.keyStore.GetKey()
var resp *clientv3.GetResponse
resp, err = c.client.Get(opCtx, key, clientv3.WithRev(0))
if err == nil {
@@ -303,7 +336,7 @@ func (c etcdTrafficClient) Request(ctx context.Context, request etcdRequestType,
if leaseID != 0 {
putCtx, putCancel := context.WithTimeout(ctx, RequestTimeout)
var resp *clientv3.PutResponse
- resp, err = c.client.PutWithLease(putCtx, c.randomKey(), fmt.Sprintf("%d", c.idProvider.NewRequestID()), leaseID)
+ resp, err = c.client.PutWithLease(putCtx, c.keyStore.GetKey(), fmt.Sprintf("%d", c.idProvider.NewRequestID()), leaseID)
putCancel()
if resp != nil {
rev = resp.Header.Revision
@@ -334,8 +367,7 @@ func (c etcdTrafficClient) Request(ctx context.Context, request etcdRequestType,
return rev, err
}
-func (c etcdTrafficClient) pickMultiTxnOps() (ops []clientv3.Op) {
- keys := rand.Perm(c.keyCount)
+func (c etcdTrafficClient) pickMultiTxnOps(keyStore *keyStore) (ops []clientv3.Op) {
opTypes := make([]model.OperationType, 4)
atLeastOnePut := false
@@ -350,8 +382,10 @@ func (c etcdTrafficClient) pickMultiTxnOps() (ops []clientv3.Op) {
opTypes[0] = model.PutOperation
}
+ keys := keyStore.GetKeysForMultiTxnOps(opTypes)
+
for i, opType := range opTypes {
- key := c.key(keys[i])
+ key := keys[i]
switch opType {
case model.RangeOperation:
ops = append(ops, clientv3.OpGet(key))
@@ -361,20 +395,12 @@ func (c etcdTrafficClient) pickMultiTxnOps() (ops []clientv3.Op) {
case model.DeleteOperation:
ops = append(ops, clientv3.OpDelete(key))
default:
- panic("unsuported choice type")
+ panic("unsupported choice type")
}
}
return ops
}
-func (c etcdTrafficClient) randomKey() string {
- return c.key(rand.Int())
-}
-
-func (c etcdTrafficClient) key(i int) string {
- return fmt.Sprintf("%s%d", c.keyPrefix, i%c.keyCount)
-}
-
func (t etcdTraffic) pickOperationType() model.OperationType {
roll := rand.Int() % 100
if roll < 10 {
diff --git a/tests/robustness/traffic/key_store.go b/tests/robustness/traffic/key_store.go
new file mode 100644
index 000000000000..a95bc194be8e
--- /dev/null
+++ b/tests/robustness/traffic/key_store.go
@@ -0,0 +1,164 @@
+// Copyright 2023 The etcd Authors
+//
+// 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 traffic
+
+import (
+ "fmt"
+ "math/rand"
+ "slices"
+ "sort"
+ "strconv"
+ "sync"
+
+ clientv3 "go.etcd.io/etcd/client/v3"
+ "go.etcd.io/etcd/tests/v3/robustness/model"
+)
+
+// Stores the key pool to use for operations. This allows keys used in Put and Delete operations to be more unique by probabilistically swapping out used keys.
+type keyStore struct {
+ mu sync.Mutex
+
+ counter int
+ keys []string
+ keyPrefix string
+ latestRevision int64
+}
+
+func NewKeyStore(size int, keyPrefix string) *keyStore {
+ k := &keyStore{
+ keys: make([]string, size),
+ counter: 0,
+ keyPrefix: keyPrefix,
+ }
+
+ // Fill with default values i.e. key0-key9
+ for ; k.counter < len(k.keys); k.counter++ {
+ k.keys[k.counter] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
+ }
+
+ return k
+}
+
+func (k *keyStore) GetKey() string {
+ k.mu.Lock()
+ defer k.mu.Unlock()
+
+ useKey := k.keys[rand.Intn(len(k.keys))]
+
+ return useKey
+}
+
+func (k *keyStore) GetKeyForDelete() string {
+ k.mu.Lock()
+ defer k.mu.Unlock()
+
+ useKeyIndex := rand.Intn(len(k.keys))
+ useKey := k.keys[useKeyIndex]
+
+ k.replaceKey(useKeyIndex)
+
+ return useKey
+}
+
+func (k *keyStore) GetKeysForMultiTxnOps(ops []model.OperationType) []string {
+ k.mu.Lock()
+ defer k.mu.Unlock()
+
+ numOps := len(ops)
+
+ if numOps > len(k.keys) {
+ panic("GetKeysForMultiTxnOps: number of operations is more than the key pool size")
+ }
+
+ keys := make([]string, numOps)
+
+ permutedKeyIndexes := rand.Perm(len(k.keys))
+ for i, op := range ops {
+ keys[i] = k.keys[permutedKeyIndexes[i]]
+
+ if op == model.DeleteOperation {
+ k.replaceKey(permutedKeyIndexes[i])
+ }
+ }
+
+ return keys
+}
+
+func (k *keyStore) GetPrefix() string {
+ k.mu.Lock()
+ defer k.mu.Unlock()
+
+ return k.keyPrefix
+}
+
+// SyncKeys reconciles our local key store with the keys currently in etcd.
+//
+// SyncKeys resolves this by:
+// 1. Getting the list request result of all the keys.
+// 2. Adding any keys that exist in etcd but are missing in the key store.
+// 3. Maintaining the key store size by removing the higher numbered keys.
+//
+// Notice that higher numbered keys will eventually be added
+// again into the keystore, so it is safe to temporarily remove them from the
+// key store.
+func (k *keyStore) SyncKeys(resp *clientv3.GetResponse) {
+ k.mu.Lock()
+ defer k.mu.Unlock()
+
+ if resp.Header.GetRevision() < k.latestRevision {
+ return
+ }
+
+ k.latestRevision = resp.Header.GetRevision()
+
+ listKeys := make([]string, len(resp.Kvs))
+ for i, kv := range resp.Kvs {
+ listKeys[i] = string(kv.Key)
+ }
+
+ for _, key := range k.keys {
+ if !slices.Contains(listKeys, key) {
+ listKeys = append(listKeys, key)
+ }
+ }
+
+ sort.Slice(listKeys, func(i, j int) bool {
+ keyNumI := k.getKeyNum(listKeys[i])
+ keyNumJ := k.getKeyNum(listKeys[j])
+ return keyNumI < keyNumJ
+ })
+
+ k.keys = listKeys[:len(k.keys)]
+
+ lastKeyNum := k.getKeyNum(k.keys[len(k.keys)-1])
+ k.counter = lastKeyNum + 1
+}
+
+func (k *keyStore) getKeyNum(key string) int {
+ if len(key) < len(k.keyPrefix) {
+ return 0
+ }
+
+ numStr := key[len(k.keyPrefix):]
+ num, _ := strconv.Atoi(numStr)
+ return num
+}
+
+func (k *keyStore) replaceKey(index int) {
+ if rand.Intn(100) < 90 {
+ k.keys[index] = fmt.Sprintf("%s%d", k.keyPrefix, k.counter)
+ k.counter++
+ }
+}
diff --git a/tests/robustness/traffic/kubernetes.go b/tests/robustness/traffic/kubernetes.go
index fe1386fa243c..e45171c03fa1 100644
--- a/tests/robustness/traffic/kubernetes.go
+++ b/tests/robustness/traffic/kubernetes.go
@@ -41,6 +41,14 @@ var (
resource: "pods",
namespace: "default",
// Please keep the sum of weights equal 100.
+ readChoices: []random.ChoiceWeight[KubernetesRequestType]{
+ {Choice: KubernetesGet, Weight: 5},
+ {Choice: KubernetesGetStale, Weight: 2},
+ {Choice: KubernetesGetRev, Weight: 8},
+ {Choice: KubernetesListStale, Weight: 5},
+ {Choice: KubernetesListAndWatch, Weight: 80},
+ },
+ // Please keep the sum of weights equal 100.
writeChoices: []random.ChoiceWeight[KubernetesRequestType]{
{Choice: KubernetesUpdate, Weight: 90},
{Choice: KubernetesDelete, Weight: 5},
@@ -53,6 +61,14 @@ var (
resource: "pods",
namespace: "default",
// Please keep the sum of weights equal 100.
+ readChoices: []random.ChoiceWeight[KubernetesRequestType]{
+ {Choice: KubernetesGet, Weight: 5},
+ {Choice: KubernetesGetStale, Weight: 2},
+ {Choice: KubernetesGetRev, Weight: 8},
+ {Choice: KubernetesListStale, Weight: 5},
+ {Choice: KubernetesListAndWatch, Weight: 80},
+ },
+ // Please keep the sum of weights equal 100.
writeChoices: []random.ChoiceWeight[KubernetesRequestType]{
{Choice: KubernetesDelete, Weight: 40},
{Choice: KubernetesCreate, Weight: 60},
@@ -64,6 +80,7 @@ type kubernetesTraffic struct {
averageKeyCount int
resource string
namespace string
+ readChoices []random.ChoiceWeight[KubernetesRequestType]
writeChoices []random.ChoiceWeight[KubernetesRequestType]
}
@@ -71,27 +88,25 @@ func (t kubernetesTraffic) ExpectUniqueRevision() bool {
return true
}
-func (t kubernetesTraffic) RunTrafficLoop(ctx context.Context, c *client.RecordingClient, limiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{}) {
+func (t kubernetesTraffic) RunKeyValueLoop(ctx context.Context, c *client.RecordingClient, p RunTrafficLoopParam) {
kc := kubernetes.Client{Client: &clientv3.Client{KV: c}}
- s := newStorage()
+ s := p.Storage
keyPrefix := "/registry/" + t.resource + "/"
g := errgroup.Group{}
- readLimit := t.averageKeyCount
g.Go(func() error {
for {
select {
case <-ctx.Done():
return ctx.Err()
- case <-finish:
+ case <-p.Finish:
return nil
default:
}
- rev, err := t.Read(ctx, kc, s, limiter, keyPrefix, readLimit)
+ err := t.Read(ctx, c, s, p.QPSLimiter, keyPrefix)
if err != nil {
continue
}
- t.Watch(ctx, c, s, limiter, keyPrefix, rev+1)
}
})
g.Go(func() error {
@@ -100,18 +115,18 @@ func (t kubernetesTraffic) RunTrafficLoop(ctx context.Context, c *client.Recordi
select {
case <-ctx.Done():
return ctx.Err()
- case <-finish:
+ case <-p.Finish:
return nil
default:
}
// Avoid multiple failed writes in a row
if lastWriteFailed {
- _, err := t.Read(ctx, kc, s, limiter, keyPrefix, 0)
+ _, err := t.List(ctx, kc, s, p.QPSLimiter, keyPrefix, t.averageKeyCount, 0)
if err != nil {
continue
}
}
- err := t.Write(ctx, kc, ids, s, limiter, nonUniqueWriteLimiter)
+ err := t.Write(ctx, kc, p.IDs, s, p.QPSLimiter, p.NonUniqueRequestConcurrencyLimiter)
lastWriteFailed = err != nil
if err != nil {
continue
@@ -121,10 +136,53 @@ func (t kubernetesTraffic) RunTrafficLoop(ctx context.Context, c *client.Recordi
g.Wait()
}
-func (t kubernetesTraffic) Read(ctx context.Context, kc kubernetes.Interface, s *storage, limiter *rate.Limiter, keyPrefix string, limit int) (rev int64, err error) {
+func (t kubernetesTraffic) RunWatchLoop(ctx context.Context, c *client.RecordingClient, p RunWatchLoopParam) {
+ runWatchLoop(ctx, c, p, watchLoopConfig{
+ key: "/registry/" + t.resource + "/",
+ requireLeader: true,
+ })
+}
+
+func (t kubernetesTraffic) Read(ctx context.Context, c *client.RecordingClient, s *storage, limiter *rate.Limiter, keyPrefix string) error {
+ kc := kubernetes.Client{Client: &clientv3.Client{KV: c}}
+ op := random.PickRandom(t.readChoices)
+ switch op {
+ case KubernetesGet:
+ key, unusedRev := s.PickRandom()
+ if unusedRev == 0 {
+ return errors.New("storage empty")
+ }
+ return t.Get(ctx, kc, s, limiter, key, 0)
+ case KubernetesGetStale:
+ key, rev := s.KeyWithUnrelatedRev()
+ return t.Get(ctx, kc, s, limiter, key, rev)
+ case KubernetesGetRev:
+ return t.Get(ctx, kc, s, limiter, "/registry/"+t.resource, 0)
+ case KubernetesListStale:
+ _, rev := s.PickRandom()
+ _, err := t.List(ctx, kc, s, limiter, keyPrefix, t.averageKeyCount, rev)
+ return err
+ case KubernetesListAndWatch:
+ rev, err := t.List(ctx, kc, s, limiter, keyPrefix, t.averageKeyCount, 0)
+ if err != nil {
+ return err
+ }
+ t.Watch(ctx, c, s, limiter, keyPrefix, rev+1)
+ return nil
+ default:
+ panic(fmt.Sprintf("invalid choice: %q", op))
+ }
+}
+
+func (t kubernetesTraffic) Get(ctx context.Context, kc kubernetes.Interface, s *storage, limiter *rate.Limiter, key string, rev int64) error {
+ _, err := kc.Get(ctx, key, kubernetes.GetOptions{Revision: rev})
+ limiter.Wait(ctx)
+ return err
+}
+
+func (t kubernetesTraffic) List(ctx context.Context, kc kubernetes.Interface, s *storage, limiter *rate.Limiter, keyPrefix string, limit int, revision int64) (rev int64, err error) {
hasMore := true
var kvs []*mvccpb.KeyValue
- var revision int64
var cont string
for hasMore {
@@ -220,17 +278,17 @@ func (t kubernetesTraffic) generateKey() string {
return fmt.Sprintf("/registry/%s/%s/%s", t.resource, t.namespace, stringutil.RandString(5))
}
-func (t kubernetesTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, interval time.Duration, finish <-chan struct{}) {
+func (t kubernetesTraffic) RunCompactLoop(ctx context.Context, c *client.RecordingClient, param RunCompactLoopParam) {
// Based on https://github.com/kubernetes/apiserver/blob/7dd4904f1896e11244ba3c5a59797697709de6b6/pkg/storage/etcd3/compact.go#L112-L127
var compactTime int64
var rev int64
var err error
for {
select {
- case <-time.After(interval):
+ case <-time.After(param.Period):
case <-ctx.Done():
return
- case <-finish:
+ case <-param.Finish:
return
}
@@ -277,9 +335,14 @@ func compact(ctx context.Context, client *client.RecordingClient, t, rev int64)
type KubernetesRequestType string
const (
- KubernetesDelete KubernetesRequestType = "delete"
- KubernetesUpdate KubernetesRequestType = "update"
- KubernetesCreate KubernetesRequestType = "create"
+ KubernetesDelete KubernetesRequestType = "delete"
+ KubernetesUpdate KubernetesRequestType = "update"
+ KubernetesCreate KubernetesRequestType = "create"
+ KubernetesGet KubernetesRequestType = "get"
+ KubernetesGetStale KubernetesRequestType = "get_stale"
+ KubernetesGetRev KubernetesRequestType = "get_rev"
+ KubernetesListStale KubernetesRequestType = "list_stale"
+ KubernetesListAndWatch KubernetesRequestType = "list_watch"
)
type storage struct {
@@ -288,7 +351,9 @@ type storage struct {
revision int64
}
-func newStorage() *storage {
+// NewKubernetesStorage creates a new storage instance for tracking Kubernetes resource revisions.
+// This storage is used by Kubernetes traffic to maintain consistency in read and write operations.
+func NewKubernetesStorage() *storage {
return &storage{
keyRevision: map[string]int64{},
}
@@ -303,9 +368,9 @@ func (s *storage) Update(resp clientv3.WatchResponse) {
}
s.revision = e.Kv.ModRevision
switch e.Type {
- case mvccpb.PUT:
+ case mvccpb.Event_PUT:
s.keyRevision[string(e.Kv.Key)] = e.Kv.ModRevision
- case mvccpb.DELETE:
+ case mvccpb.Event_DELETE:
delete(s.keyRevision, string(e.Kv.Key))
}
}
@@ -333,7 +398,15 @@ func (s *storage) Count() int {
func (s *storage) PickRandom() (key string, rev int64) {
s.mux.RLock()
defer s.mux.RUnlock()
- n := rand.Intn(len(s.keyRevision))
+ return s.pickRandomLocked()
+}
+
+func (s *storage) pickRandomLocked() (key string, rev int64) {
+ l := len(s.keyRevision)
+ if l == 0 {
+ return "", 0
+ }
+ n := rand.Intn(l)
i := 0
for k, v := range s.keyRevision {
if i == n {
@@ -343,3 +416,15 @@ func (s *storage) PickRandom() (key string, rev int64) {
}
return "", 0
}
+
+func (s *storage) KeyWithUnrelatedRev() (key string, rev int64) {
+ s.mux.RLock()
+ defer s.mux.RUnlock()
+ l := len(s.keyRevision)
+ if l == 0 {
+ return "", 0
+ }
+ key, _ = s.pickRandomLocked()
+ _, rev = s.pickRandomLocked()
+ return key, rev
+}
diff --git a/tests/robustness/traffic/traffic.go b/tests/robustness/traffic/traffic.go
index c66aa4543620..04159c6ecc99 100644
--- a/tests/robustness/traffic/traffic.go
+++ b/tests/robustness/traffic/traffic.go
@@ -24,90 +24,121 @@ import (
"go.uber.org/zap"
"golang.org/x/time/rate"
+ clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/e2e"
"go.etcd.io/etcd/tests/v3/robustness/client"
"go.etcd.io/etcd/tests/v3/robustness/identity"
"go.etcd.io/etcd/tests/v3/robustness/model"
+ "go.etcd.io/etcd/tests/v3/robustness/random"
"go.etcd.io/etcd/tests/v3/robustness/report"
+ "go.etcd.io/etcd/tests/v3/robustness/validate"
)
+type Range struct {
+ Min int64
+ Max int64
+}
+
+func (r Range) Rand() int64 {
+ if r.Min == r.Max {
+ return r.Min
+ }
+ return random.RandRange(r.Min, r.Max+1)
+}
+
var (
DefaultLeaseTTL int64 = 7200
RequestTimeout = 200 * time.Millisecond
- WatchTimeout = time.Second
+ WatchTimeout = 500 * time.Millisecond
MultiOpTxnOpCount = 4
- DefaultCompactionPeriod = 200 * time.Millisecond
+ MinimalCompactionPeriod = 100 * time.Millisecond
- LowTraffic = Profile{
+ KeyValueVeryLow = KeyValue{
+ MinimalQPS: 50,
+ MaximalQPS: 100,
+ BurstableQPS: 100,
+ MemberClientCount: 6,
+ ClusterClientCount: 2,
+ MaxNonUniqueRequestConcurrency: 3,
+ }
+ KeyValueMedium = KeyValue{
MinimalQPS: 100,
MaximalQPS: 200,
BurstableQPS: 1000,
- ClientCount: 8,
+ MemberClientCount: 6,
+ ClusterClientCount: 2,
MaxNonUniqueRequestConcurrency: 3,
}
- HighTrafficProfile = Profile{
+ KeyValueHigh = KeyValue{
MinimalQPS: 100,
MaximalQPS: 1000,
BurstableQPS: 1000,
- ClientCount: 8,
+ MemberClientCount: 6,
+ ClusterClientCount: 2,
MaxNonUniqueRequestConcurrency: 3,
}
+ WatchDefault = Watch{
+ MemberClientCount: 6,
+ ClusterClientCount: 2,
+ RevisionOffsetRange: Range{Min: -100, Max: 100},
+ }
+ CompactionDefault = Compaction{
+ Period: 200 * time.Millisecond,
+ }
+ CompactionFrequent = Compaction{
+ Period: 100 * time.Millisecond,
+ }
)
-func SimulateTraffic(ctx context.Context, t *testing.T, lg *zap.Logger, clus *e2e.EtcdProcessCluster, profile Profile, traffic Traffic, failpointInjected <-chan report.FailpointInjection, baseTime time.Time, ids identity.Provider) []report.ClientReport {
- mux := sync.Mutex{}
+func SimulateTraffic(ctx context.Context, t *testing.T, lg *zap.Logger, clus *e2e.EtcdProcessCluster, profile Profile, traffic Traffic, failpointInjected <-chan report.FailpointInjection, clientSet *client.ClientSet) []report.ClientReport {
endpoints := clus.EndpointsGRPC()
lm := identity.NewLeaseIDStorage()
- reports := []report.ClientReport{}
// Use the highest MaximalQPS of all traffic profiles as burst otherwise actual traffic may be accidentally limited
- limiter := rate.NewLimiter(rate.Limit(profile.MaximalQPS), profile.BurstableQPS)
+ limiter := rate.NewLimiter(rate.Limit(profile.KeyValue.MaximalQPS), profile.KeyValue.BurstableQPS)
- cc, err := client.NewRecordingClient(endpoints, ids, baseTime)
+ err := CheckEmptyDatabaseAtStart(ctx, lg, endpoints, clientSet)
require.NoError(t, err)
- defer cc.Close()
- // Ensure that first operation succeeds
- _, err = cc.Put(ctx, "start", "true")
- require.NoErrorf(t, err, "First operation failed, validation requires first operation to succeed")
+
wg := sync.WaitGroup{}
- nonUniqueWriteLimiter := NewConcurrencyLimiter(profile.MaxNonUniqueRequestConcurrency)
+ nonUniqueWriteLimiter := NewConcurrencyLimiter(profile.KeyValue.MaxNonUniqueRequestConcurrency)
finish := make(chan struct{})
- lg.Info("Start traffic")
- startTime := time.Since(baseTime)
- for i := 0; i < profile.ClientCount; i++ {
- wg.Add(1)
- c, nerr := client.NewRecordingClient([]string{endpoints[i%len(endpoints)]}, ids, baseTime)
- require.NoError(t, nerr)
- go func(c *client.RecordingClient) {
- defer wg.Done()
- defer c.Close()
- traffic.RunTrafficLoop(ctx, c, limiter, ids, lm, nonUniqueWriteLimiter, finish)
- mux.Lock()
- reports = append(reports, c.Report())
- mux.Unlock()
- }(c)
+ keyStore := NewKeyStore(10, "key")
+ kubernetesStorage := NewKubernetesStorage()
+
+ lg.Info("Start traffic")
+ startTime := time.Since(clientSet.BaseTime())
+ err = SimulateKeyValueTraffic(ctx, &wg, profile.KeyValue, endpoints, clientSet, traffic, RunTrafficLoopParam{
+ QPSLimiter: limiter,
+ IDs: clientSet.IdentityProvider(),
+ LeaseIDStorage: lm,
+ NonUniqueRequestConcurrencyLimiter: nonUniqueWriteLimiter,
+ KeyStore: keyStore,
+ Storage: kubernetesStorage,
+ Finish: finish,
+ })
+ require.NoError(t, err)
+ if profile.Watch != nil {
+ err = SimulateWatchTraffic(ctx, &wg, profile.Watch, endpoints, clientSet, traffic, RunWatchLoopParam{
+ Config: *profile.Watch,
+ QPSLimiter: limiter,
+ KeyStore: keyStore,
+ Storage: kubernetesStorage,
+ Finish: finish,
+ Logger: lg,
+ })
+ require.NoError(t, err)
}
- if !profile.ForbidCompaction {
- wg.Add(1)
- c, nerr := client.NewRecordingClient(endpoints, ids, baseTime)
- if nerr != nil {
- t.Fatal(nerr)
+ if profile.Compaction != nil {
+ if profile.Compaction.Period < MinimalCompactionPeriod {
+ t.Fatalf("Compaction period %v below minimal %v", profile.Compaction.Period, MinimalCompactionPeriod)
}
- go func(c *client.RecordingClient) {
- defer wg.Done()
- defer c.Close()
-
- compactionPeriod := DefaultCompactionPeriod
- if profile.CompactPeriod != time.Duration(0) {
- compactionPeriod = profile.CompactPeriod
- }
-
- traffic.RunCompactLoop(ctx, c, compactionPeriod, finish)
- mux.Lock()
- reports = append(reports, c.Report())
- mux.Unlock()
- }(c)
+ err = SimulateCompactionTraffic(ctx, &wg, profile.Compaction, endpoints, clientSet, traffic, RunCompactLoopParam{
+ Period: profile.Compaction.Period,
+ Finish: finish,
+ })
+ require.NoError(t, err)
}
var fr *report.FailpointInjection
select {
@@ -120,33 +151,186 @@ func SimulateTraffic(ctx context.Context, t *testing.T, lg *zap.Logger, clus *e2
close(finish)
wg.Wait()
lg.Info("Finished traffic")
- endTime := time.Since(baseTime)
+ endTime := time.Since(clientSet.BaseTime())
time.Sleep(time.Second)
// Ensure that last operation succeeds
+ cc, err := clientSet.NewClient(endpoints)
+ require.NoError(t, err)
+ defer cc.Close()
_, err = cc.Put(ctx, "tombstone", "true")
require.NoErrorf(t, err, "Last operation failed, validation requires last operation to succeed")
- reports = append(reports, cc.Report())
+ reports := clientSet.Reports()
+
+ totalStats := CalculateStats(reports, startTime, endTime)
+ beforeFailpointStats := CalculateStats(reports, startTime, fr.Start)
+ duringFailpointStats := CalculateStats(reports, fr.Start, fr.End)
+ afterFailpointStats := CalculateStats(reports, fr.End, endTime)
- totalStats := calculateStats(reports, startTime, endTime)
- beforeFailpointStats := calculateStats(reports, startTime, fr.Start)
- duringFailpointStats := calculateStats(reports, fr.Start, fr.End)
- afterFailpointStats := calculateStats(reports, fr.End, endTime)
+ lg.Info("Reporting complete traffic", zap.Int("successes", totalStats.Successes), zap.Int("failures", totalStats.Failures), zap.Float64("successRate", totalStats.SuccessRate()), zap.Duration("period", totalStats.Period), zap.Float64("qps", totalStats.QPS()))
+ lg.Info("Reporting traffic before failure injection", zap.Int("successes", beforeFailpointStats.Successes), zap.Int("failures", beforeFailpointStats.Failures), zap.Float64("successRate", beforeFailpointStats.SuccessRate()), zap.Duration("period", beforeFailpointStats.Period), zap.Float64("qps", beforeFailpointStats.QPS()))
+ lg.Info("Reporting traffic during failure injection", zap.Int("successes", duringFailpointStats.Successes), zap.Int("failures", duringFailpointStats.Failures), zap.Float64("successRate", duringFailpointStats.SuccessRate()), zap.Duration("period", duringFailpointStats.Period), zap.Float64("qps", duringFailpointStats.QPS()))
+ lg.Info("Reporting traffic after failure injection", zap.Int("successes", afterFailpointStats.Successes), zap.Int("failures", afterFailpointStats.Failures), zap.Float64("successRate", afterFailpointStats.SuccessRate()), zap.Duration("period", afterFailpointStats.Period), zap.Float64("qps", afterFailpointStats.QPS()))
- lg.Info("Reporting complete traffic", zap.Int("successes", totalStats.successes), zap.Int("failures", totalStats.failures), zap.Float64("successRate", totalStats.successRate()), zap.Duration("period", totalStats.period), zap.Float64("qps", totalStats.QPS()))
- lg.Info("Reporting traffic before failure injection", zap.Int("successes", beforeFailpointStats.successes), zap.Int("failures", beforeFailpointStats.failures), zap.Float64("successRate", beforeFailpointStats.successRate()), zap.Duration("period", beforeFailpointStats.period), zap.Float64("qps", beforeFailpointStats.QPS()))
- lg.Info("Reporting traffic during failure injection", zap.Int("successes", duringFailpointStats.successes), zap.Int("failures", duringFailpointStats.failures), zap.Float64("successRate", duringFailpointStats.successRate()), zap.Duration("period", duringFailpointStats.period), zap.Float64("qps", duringFailpointStats.QPS()))
- lg.Info("Reporting traffic after failure injection", zap.Int("successes", afterFailpointStats.successes), zap.Int("failures", afterFailpointStats.failures), zap.Float64("successRate", afterFailpointStats.successRate()), zap.Duration("period", afterFailpointStats.period), zap.Float64("qps", afterFailpointStats.QPS()))
+ watchTotal := CalculateWatchStats(reports, startTime, endTime)
+ lg.Info("Reporting complete watch", zap.Int("requests", watchTotal.Requests), zap.Int("events", watchTotal.Events), zap.Float64("eventsQPS", watchTotal.EventsQPS()), zap.Int("progressNotifies", watchTotal.ProgressNotifies), zap.Int("immediateClosures", watchTotal.ImmediateClosures), zap.Duration("period", watchTotal.Period), zap.Duration("avgDuration", watchTotal.AvgDuration()))
- if beforeFailpointStats.QPS() < profile.MinimalQPS {
- t.Errorf("Requiring minimal %f qps before failpoint injection for test results to be reliable, got %f qps", profile.MinimalQPS, beforeFailpointStats.QPS())
+ if beforeFailpointStats.QPS() < profile.KeyValue.MinimalQPS {
+ t.Errorf("Requiring minimal %f qps before failpoint injection for test results to be reliable, got %f qps", profile.KeyValue.MinimalQPS, beforeFailpointStats.QPS())
}
- // TODO: Validate QPS post failpoint injection to ensure the that we sufficiently cover period when cluster recovers.
+ // TODO: Validate QPS post failpoint injection to ensure that we sufficiently cover the period when the cluster recovers.
return reports
}
-func calculateStats(reports []report.ClientReport, start, end time.Duration) (ts trafficStats) {
- ts.period = end - start
+func SimulateKeyValueTraffic(ctx context.Context, wg *sync.WaitGroup, profile *KeyValue, endpoints []string, clientSet *client.ClientSet, tf Traffic, baseParam RunTrafficLoopParam) error {
+ for i := range profile.MemberClientCount {
+ c, err := clientSet.NewClient([]string{endpoints[i%len(endpoints)]})
+ if err != nil {
+ return err
+ }
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+
+ tf.RunKeyValueLoop(ctx, c, baseParam)
+ }(c)
+ }
+ for range profile.ClusterClientCount {
+ c, err := clientSet.NewClient(endpoints)
+ if err != nil {
+ return err
+ }
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+
+ tf.RunKeyValueLoop(ctx, c, baseParam)
+ }(c)
+ }
+ return nil
+}
+
+func SimulateWatchTraffic(ctx context.Context, wg *sync.WaitGroup, profile *Watch, endpoints []string, clientSet *client.ClientSet, tf Traffic, baseParam RunWatchLoopParam) error {
+ for i := range profile.MemberClientCount {
+ c, err := clientSet.NewClient([]string{endpoints[i%len(endpoints)]})
+ if err != nil {
+ return err
+ }
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+ tf.RunWatchLoop(ctx, c, baseParam)
+ }(c)
+ }
+ for range profile.ClusterClientCount {
+ c, err := clientSet.NewClient(endpoints)
+ if err != nil {
+ return err
+ }
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+ tf.RunWatchLoop(ctx, c, baseParam)
+ }(c)
+ }
+ return nil
+}
+
+func SimulateCompactionTraffic(ctx context.Context, wg *sync.WaitGroup, profile *Compaction, endpoints []string, clientSet *client.ClientSet, tf Traffic, baseParam RunCompactLoopParam) error {
+ c, err := clientSet.NewClient(endpoints)
+ if err != nil {
+ return err
+ }
+ wg.Add(1)
+ go func(c *client.RecordingClient) {
+ defer wg.Done()
+ defer c.Close()
+ tf.RunCompactLoop(ctx, c, baseParam)
+ }(c)
+ return nil
+}
+
+func CalculateWatchStats(reports []report.ClientReport, start, end time.Duration) (ws watchStats) {
+ ws.Period = end - start
+ if ws.Period <= 0 {
+ return ws
+ }
+
+ for _, r := range reports {
+ for _, w := range r.Watch {
+ var (
+ firstInWindow time.Duration
+ lastInWindow time.Duration
+ haveInWindow bool
+ noEventsYetInWindow = true
+ closedCounted = false
+ )
+
+ for _, resp := range w.Responses {
+ if resp.Time < start || resp.Time > end {
+ continue
+ }
+ if !haveInWindow {
+ firstInWindow = resp.Time
+ haveInWindow = true
+ }
+ lastInWindow = resp.Time
+ if resp.IsProgressNotify {
+ ws.ProgressNotifies++
+ }
+ if len(resp.Events) > 0 {
+ ws.Events += len(resp.Events)
+ noEventsYetInWindow = false
+ }
+ if resp.Error != "" && noEventsYetInWindow && !closedCounted {
+ ws.ImmediateClosures++
+ closedCounted = true
+ }
+ }
+
+ if haveInWindow {
+ ws.Requests++
+ if lastInWindow > firstInWindow {
+ ws.SumDuration += lastInWindow - firstInWindow
+ ws.DurationsCount++
+ }
+ }
+ }
+ }
+
+ return ws
+}
+
+type watchStats struct {
+ Period time.Duration
+ Requests int
+ Events int
+ ProgressNotifies int
+ ImmediateClosures int
+ SumDuration time.Duration
+ DurationsCount int
+}
+
+func (ws *watchStats) AvgDuration() time.Duration {
+ if ws.DurationsCount == 0 {
+ return 0
+ }
+ return ws.SumDuration / time.Duration(ws.DurationsCount)
+}
+
+func (ws *watchStats) EventsQPS() float64 {
+ if ws.Period <= 0 {
+ return 0
+ }
+ return float64(ws.Events) / ws.Period.Seconds()
+}
+
+func CalculateStats(reports []report.ClientReport, start, end time.Duration) (ts trafficStats) {
+ ts.Period = end - start
for _, r := range reports {
for _, op := range r.KeyValue {
@@ -155,9 +339,9 @@ func calculateStats(reports []report.ClientReport, start, end time.Duration) (ts
}
resp := op.Output.(model.MaybeEtcdResponse)
if resp.Error == "" {
- ts.successes++
+ ts.Successes++
} else {
- ts.failures++
+ ts.Failures++
}
}
}
@@ -165,40 +349,148 @@ func calculateStats(reports []report.ClientReport, start, end time.Duration) (ts
}
type trafficStats struct {
- successes, failures int
- period time.Duration
+ Successes, Failures int
+ Period time.Duration
}
-func (ts *trafficStats) successRate() float64 {
- return float64(ts.successes) / float64(ts.successes+ts.failures)
+func (ts *trafficStats) SuccessRate() float64 {
+ return float64(ts.Successes) / float64(ts.Successes+ts.Failures)
}
func (ts *trafficStats) QPS() float64 {
- return float64(ts.successes) / ts.period.Seconds()
+ return float64(ts.Successes) / ts.Period.Seconds()
}
type Profile struct {
+ KeyValue *KeyValue
+ Watch *Watch
+ Compaction *Compaction
+}
+
+type KeyValue struct {
MinimalQPS float64
MaximalQPS float64
BurstableQPS int
MaxNonUniqueRequestConcurrency int
- ClientCount int
- ForbidCompaction bool
- CompactPeriod time.Duration
+ MemberClientCount int
+ ClusterClientCount int
+}
+
+type Watch struct {
+ MemberClientCount int
+ ClusterClientCount int
+ RevisionOffsetRange Range
+}
+
+type Compaction struct {
+ Period time.Duration
+}
+
+type RunTrafficLoopParam struct {
+ QPSLimiter *rate.Limiter
+ IDs identity.Provider
+ LeaseIDStorage identity.LeaseIDStorage
+ NonUniqueRequestConcurrencyLimiter ConcurrencyLimiter
+ KeyStore *keyStore
+ Storage *storage
+ Finish <-chan struct{}
}
-func (p Profile) WithoutCompaction() Profile {
- p.ForbidCompaction = true
- return p
+type RunCompactLoopParam struct {
+ Period time.Duration
+ Finish <-chan struct{}
}
-func (p Profile) WithCompactionPeriod(cp time.Duration) Profile {
- p.CompactPeriod = cp
- return p
+type RunWatchLoopParam struct {
+ Config Watch
+ QPSLimiter *rate.Limiter
+ // TODO: merge 2 key stores into 1
+ KeyStore *keyStore
+ Storage *storage
+ Finish <-chan struct{}
+ Logger *zap.Logger
}
type Traffic interface {
- RunTrafficLoop(ctx context.Context, c *client.RecordingClient, qpsLimiter *rate.Limiter, ids identity.Provider, lm identity.LeaseIDStorage, nonUniqueWriteLimiter ConcurrencyLimiter, finish <-chan struct{})
- RunCompactLoop(ctx context.Context, c *client.RecordingClient, period time.Duration, finish <-chan struct{})
+ RunKeyValueLoop(ctx context.Context, c *client.RecordingClient, param RunTrafficLoopParam)
+ RunWatchLoop(ctx context.Context, c *client.RecordingClient, param RunWatchLoopParam)
+ RunCompactLoop(ctx context.Context, c *client.RecordingClient, param RunCompactLoopParam)
ExpectUniqueRevision() bool
}
+
+func runWatchLoop(ctx context.Context, c *client.RecordingClient, p RunWatchLoopParam, cfg watchLoopConfig) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-p.Finish:
+ return
+ default:
+ }
+ err := p.QPSLimiter.Wait(ctx)
+ if err != nil {
+ return
+ }
+ // Client.get may fail when the blackhole is injected.
+ // We suppress logging for these expected failures to avoid polluting the logs.
+ _ = runWatch(ctx, c, p, cfg)
+ }
+}
+
+func runWatch(ctx context.Context, c *client.RecordingClient, p RunWatchLoopParam, cfg watchLoopConfig) error {
+ getCtx, getCancel := context.WithTimeout(ctx, RequestTimeout)
+ defer getCancel()
+
+ resp, err := c.Get(getCtx, cfg.key)
+ if err != nil {
+ return err
+ }
+ rev := resp.Header.Revision + p.Config.RevisionOffsetRange.Rand()
+
+ watchCtx, watchCancel := context.WithTimeout(ctx, WatchTimeout)
+ defer watchCancel()
+
+ if cfg.requireLeader {
+ watchCtx = clientv3.WithRequireLeader(watchCtx)
+ }
+ w := c.Watch(watchCtx, cfg.key, rev, true, true, true)
+ for {
+ select {
+ case <-ctx.Done():
+ return nil
+ case <-p.Finish:
+ return nil
+ case _, ok := <-w:
+ if !ok {
+ return nil
+ }
+ }
+ }
+}
+
+type watchLoopConfig struct {
+ key string
+ requireLeader bool
+}
+
+func CheckEmptyDatabaseAtStart(ctx context.Context, lg *zap.Logger, endpoints []string, cs *client.ClientSet) error {
+ c, err := cs.NewClient(endpoints)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+ for {
+ rCtx, cancel := context.WithTimeout(ctx, RequestTimeout)
+ resp, err := c.Get(rCtx, "key")
+ cancel()
+ if err != nil {
+ lg.Warn("Failed to check if database empty at start, retrying", zap.Error(err))
+ continue
+ }
+ if resp.Header.Revision != 1 {
+ return validate.ErrNotEmptyDatabase
+ }
+ break
+ }
+ return nil
+}
diff --git a/tests/robustness/validate/operations.go b/tests/robustness/validate/operations.go
index 9f39407ad818..d86f60fff26b 100644
--- a/tests/robustness/validate/operations.go
+++ b/tests/robustness/validate/operations.go
@@ -24,7 +24,6 @@ import (
"go.uber.org/zap"
"go.etcd.io/etcd/tests/v3/robustness/model"
- "go.etcd.io/etcd/tests/v3/robustness/report"
)
var (
@@ -32,32 +31,71 @@ var (
errFutureRevRespRequested = errors.New("request about a future rev with response")
)
-func validateLinearizableOperationsAndVisualize(lg *zap.Logger, operations []porcupine.Operation, timeout time.Duration) (result porcupine.CheckResult, visualize func(basepath string) error) {
+func validateLinearizableOperationsAndVisualize(lg *zap.Logger, keys []string, operations []porcupine.Operation, timeout time.Duration) LinearizationResult {
lg.Info("Validating linearizable operations", zap.Duration("timeout", timeout))
start := time.Now()
- result, info := porcupine.CheckOperationsVerbose(model.NonDeterministicModel, operations, timeout)
- switch result {
- case porcupine.Illegal:
- lg.Error("Linearization failed", zap.Duration("duration", time.Since(start)))
- case porcupine.Unknown:
- lg.Error("Linearization has timed out", zap.Duration("duration", time.Since(start)))
+
+ model.LinearizationDeadlineTripped.Store(0)
+
+ var timer *time.Timer
+ if timeout > 0 {
+ // Porcupine timeout is not always enforced (see https://github.com/anishathalye/porcupine/issues/44)
+ // Give it a small grace period before forcing the deadline from inside model execution.
+ timer = time.AfterFunc(timeout*11/10, func() {
+ model.LinearizationDeadlineTripped.Store(1)
+ })
+ }
+
+ m := model.NonDeterministicModel(keys)
+ check, info := porcupine.CheckOperationsVerbose(m, operations, timeout)
+ if timer != nil {
+ timer.Stop()
+ }
+ duration := time.Since(start)
+
+ result := LinearizationResult{
+ Info: info,
+ Model: m,
+ }
+
+ if model.LinearizationDeadlineTripped.Load() != 0 {
+ result.Status = DeadlineExceeded
+ result.Message = "deadline exceeded"
+ lg.Error("Linearization deadline exceeded", zap.Duration("duration", duration))
+ return result
+ }
+
+ switch check {
case porcupine.Ok:
- lg.Info("Linearization success", zap.Duration("duration", time.Since(start)))
+ result.Status = Success
+ lg.Info("Linearization success", zap.Duration("duration", duration))
+ case porcupine.Unknown:
+ result.Status = Timeout
+ lg.Error("Linearization timed out", zap.Duration("duration", duration))
+ case porcupine.Illegal:
+ result.Status = Failure
+ result.Message = "illegal"
+ lg.Error("Linearization illegal", zap.Duration("duration", duration))
default:
- panic(fmt.Sprintf("Unknown Linearization result %s", result))
- }
- return result, func(path string) error {
- lg.Info("Saving visualization", zap.String("path", path))
- err := porcupine.VisualizePath(model.NonDeterministicModel, info, path)
- if err != nil {
- return fmt.Errorf("failed to visualize, err: %w", err)
- }
- return nil
+ result.Status = Failure
+ result.Message = fmt.Sprintf("unknown results from porcupine: %s", check)
}
+ return result
}
-func validateSerializableOperations(lg *zap.Logger, operations []porcupine.Operation, replay *model.EtcdReplay) (lastErr error) {
+func validateSerializableOperations(lg *zap.Logger, operations []porcupine.Operation, replay *model.EtcdReplay) Result {
lg.Info("Validating serializable operations")
+ start := time.Now()
+ err := validateSerializableOperationsError(lg, operations, replay)
+ if err != nil {
+ lg.Error("Serializable validation failed", zap.Duration("duration", time.Since(start)), zap.Error(err))
+ } else {
+ lg.Info("Serializable validation success", zap.Duration("duration", time.Since(start)))
+ }
+ return ResultFromError(err)
+}
+
+func validateSerializableOperationsError(lg *zap.Logger, operations []porcupine.Operation, replay *model.EtcdReplay) (lastErr error) {
for _, read := range operations {
request := read.Input.(model.EtcdRequest)
response := read.Output.(model.MaybeEtcdResponse)
@@ -69,19 +107,6 @@ func validateSerializableOperations(lg *zap.Logger, operations []porcupine.Opera
return lastErr
}
-func filterSerializableOperations(clients []report.ClientReport) []porcupine.Operation {
- resp := []porcupine.Operation{}
- for _, client := range clients {
- for _, op := range client.KeyValue {
- request := op.Input.(model.EtcdRequest)
- if request.Type == model.Range && request.Range.Revision != 0 {
- resp = append(resp, op)
- }
- }
- }
- return resp
-}
-
func validateSerializableRead(lg *zap.Logger, replay *model.EtcdReplay, request model.EtcdRequest, response model.MaybeEtcdResponse) error {
if response.Persisted || response.Error != "" {
return nil
diff --git a/tests/robustness/validate/operations_test.go b/tests/robustness/validate/operations_test.go
index a244d9f75339..21f366769871 100644
--- a/tests/robustness/validate/operations_test.go
+++ b/tests/robustness/validate/operations_test.go
@@ -17,9 +17,12 @@ package validate
import (
"fmt"
+ "math/rand/v2"
"testing"
+ "time"
"github.com/anishathalye/porcupine"
+ "go.uber.org/zap"
"go.uber.org/zap/zaptest"
"go.etcd.io/etcd/tests/v3/robustness/model"
@@ -238,13 +241,9 @@ func TestValidateSerializableOperations(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
replay := model.NewReplay(tc.persistedRequests)
- err := validateSerializableOperations(zaptest.NewLogger(t), tc.operations, replay)
- var errStr string
- if err != nil {
- errStr = err.Error()
- }
- if errStr != tc.expectError {
- t.Errorf("validateSerializableOperations(...), got: %q, want: %q", err, tc.expectError)
+ result := validateSerializableOperations(zaptest.NewLogger(t), tc.operations, replay)
+ if result.Message != tc.expectError {
+ t.Errorf("validateSerializableOperations(...), got: %q, want: %q", result.Message, tc.expectError)
}
})
}
@@ -294,3 +293,235 @@ func keyValueRevision(key, value string, rev int64) model.KeyValue {
},
}
}
+
+func TestValidateLinearizableOperationsTimeoutIsRespected(t *testing.T) {
+ timeout := time.Second
+ const failedPutCount = 17
+ // Repeat the range read to make the final applyRequestWithResponse step slow.
+ const rangeReadCount = 3072
+
+ history := []porcupine.Operation{}
+ for i := 0; i < failedPutCount; i++ {
+ history = append(history, porcupine.Operation{
+ ClientId: i,
+ Input: putRequest(fmt.Sprintf("failed%03d", i), "value"),
+ Output: errorResponse(fmt.Errorf("timeout")),
+ Call: int64(i),
+ Return: int64(failedPutCount + i),
+ })
+ }
+
+ rangeOperations := make([]model.EtcdOperation, 0, rangeReadCount)
+ for i := 0; i < rangeReadCount; i++ {
+ rangeOperations = append(rangeOperations, model.EtcdOperation{
+ Type: model.RangeOperation,
+ Range: model.RangeOptions{
+ Start: "failed",
+ End: "faile~",
+ },
+ })
+ }
+ readRequest := model.EtcdRequest{
+ Type: model.Txn,
+ Txn: &model.TxnRequest{
+ OperationsOnSuccess: rangeOperations,
+ },
+ }
+
+ // Each failed put can be lost or persisted, so the large read-only
+ // transaction reaches applyRequestWithResponse with many possible states in
+ // a single Step call.
+ replay := model.NewReplayFromOperations(history)
+ state, err := replay.StateForRevision(failedPutCount + 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, readResponse := state.Step(readRequest)
+ history = append(history, porcupine.Operation{
+ ClientId: failedPutCount,
+ Input: readRequest,
+ Output: readResponse,
+ Call: int64(2 * failedPutCount),
+ Return: int64(2*failedPutCount + 1),
+ })
+ keys := model.ModelKeys(history)
+
+ start := time.Now()
+ result := validateLinearizableOperationsAndVisualize(zap.NewNop(), keys, history, timeout)
+ elapsed := time.Since(start)
+
+ if result.Status != DeadlineExceeded {
+ t.Fatalf("validateLinearizableOperationsAndVisualize(...) status = %q, want %q", result.Status, DeadlineExceeded)
+ }
+ if result.Message != "deadline exceeded" {
+ t.Fatalf("validateLinearizableOperationsAndVisualize(...) message = %q, want %q", result.Message, "deadline exceeded")
+ }
+ if elapsed > timeout+250*time.Millisecond {
+ t.Fatalf("validateLinearizableOperationsAndVisualize(...) does not respect timeout: %v, timeout was %v", elapsed, timeout)
+ }
+}
+
+func BenchmarkValidateLinearizableOperations(b *testing.B) {
+ lg := zap.NewNop()
+ b.Run("SequentialSuccessPuts", func(b *testing.B) {
+ history := sequentialSuccessPuts(7000, 2)
+ shuffles := shuffleHistory(history, b.N)
+ b.ResetTimer()
+ validateShuffles(b, lg, shuffles, time.Second)
+ })
+ b.Run("SequentialFailedPuts", func(b *testing.B) {
+ history := sequentialFailedPuts(14, 1)
+ shuffles := shuffleHistory(history, b.N)
+ b.ResetTimer()
+ validateShuffles(b, lg, shuffles, time.Second)
+ })
+ b.Run("ConcurrentFailedPutsWithRead", func(b *testing.B) {
+ history := concurrentFailedPutsWithRead(b, 13)
+ shuffles := shuffleHistory(history, b.N)
+ b.ResetTimer()
+ validateShuffles(b, lg, shuffles, time.Second)
+ })
+ b.Run("BacktrackingHeavy", func(b *testing.B) {
+ history := backtrackingHeavy(b)
+ shuffles := shuffleHistory(history, b.N)
+ keys := model.ModelKeys(history)
+ b.ResetTimer()
+ for i := 0; i < len(shuffles); i++ {
+ validateLinearizableOperationsAndVisualize(lg, keys, shuffles[i], time.Second)
+ }
+ })
+}
+
+func sequentialSuccessPuts(count int, startRevision int64) []porcupine.Operation {
+ ops := []porcupine.Operation{}
+ for i := 0; i < count; i++ {
+ ops = append(ops, porcupine.Operation{
+ ClientId: i,
+ Input: putRequest("key", "value"),
+ Output: txnResponse(startRevision+int64(i), model.EtcdOperationResult{}),
+ Call: int64(i * 2),
+ Return: int64(i*2 + 1),
+ })
+ }
+ return ops
+}
+
+func concurrentFailedPutsWithRead(b *testing.B, concurrencyCount int) []porcupine.Operation {
+ ops := []porcupine.Operation{}
+ for i := 0; i < concurrencyCount; i++ {
+ ops = append(ops, porcupine.Operation{
+ ClientId: i,
+ Input: putRequest("key", "value"),
+ Output: errorResponse(fmt.Errorf("timeout")),
+ Call: int64(i),
+ Return: int64(i) + int64(concurrencyCount),
+ })
+ }
+ replay := model.NewReplayFromOperations(ops)
+ state, err := replay.StateForRevision(int64(concurrencyCount) + 1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ request := rangeRequest("key", "kez", 0, 0)
+ _, resp := state.Step(request)
+ ops = append(ops, porcupine.Operation{
+ ClientId: 0,
+ Input: request,
+ Output: resp,
+ Call: int64(concurrencyCount) + 1,
+ Return: int64(concurrencyCount) + 2,
+ })
+ return ops
+}
+
+func sequentialFailedPuts(count int, keyCount int) []porcupine.Operation {
+ ops := []porcupine.Operation{}
+ for i := 0; i < count; i++ {
+ key := "key0"
+ if keyCount > 1 {
+ key = fmt.Sprintf("key%d", i%keyCount)
+ }
+ ops = append(ops, porcupine.Operation{
+ ClientId: i,
+ Input: putRequest(key, "value"),
+ Output: errorResponse(fmt.Errorf("timeout")),
+ Call: int64(i * 2),
+ Return: int64(i*2 + 1),
+ })
+ }
+ return ops
+}
+
+func backtrackingHeavy(b *testing.B) (ops []porcupine.Operation) {
+ for i := 0; i < 30; i++ {
+ ops = append(ops, porcupine.Operation{
+ ClientId: -1,
+ Input: putRequest(fmt.Sprintf("key%d", i+1000), "value"),
+ Output: txnResponse(int64(i+2), model.EtcdOperationResult{}),
+ Call: int64(i),
+ Return: int64(i) + 1,
+ })
+ }
+ startTime := int64(1000)
+
+ failedPuts := 4
+ for i := 0; i < failedPuts; i++ {
+ ops = append(ops, porcupine.Operation{
+ ClientId: i,
+ Input: putRequest(fmt.Sprintf("key%d", i), "value"),
+ Output: errorResponse(fmt.Errorf("timeout")),
+ Call: startTime + int64(i),
+ Return: startTime + 1000 + int64(i),
+ })
+ }
+ replay := model.NewReplayFromOperations(ops)
+ state, err := replay.StateForRevision(int64(30 + 1))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ concurrentReads := 3
+ for i := 0; i < concurrentReads; i++ {
+ request := rangeRequest(fmt.Sprintf("key%d", i), "", 0, 0)
+ _, resp := state.Step(request)
+ ops = append(ops, porcupine.Operation{
+ ClientId: failedPuts + i,
+ Input: request,
+ Output: resp,
+ Call: startTime + 1100,
+ Return: startTime + 2100,
+ })
+ }
+
+ ops = append(ops, porcupine.Operation{
+ ClientId: 99,
+ Input: rangeRequest("key0", "", 0, 0),
+ Output: rangeResponse(0, keyValueRevision("key0", "wrong", 9999)),
+ Call: startTime + 3000,
+ Return: startTime + 4000,
+ })
+ return ops
+}
+
+func shuffleHistory(history []porcupine.Operation, shuffleCount int) [][]porcupine.Operation {
+ shuffles := make([][]porcupine.Operation, shuffleCount)
+ for i := 0; i < shuffleCount; i++ {
+ historyCopy := make([]porcupine.Operation, len(history))
+ copy(historyCopy, history)
+ rand.Shuffle(len(historyCopy), func(i, j int) {
+ historyCopy[i], historyCopy[j] = historyCopy[j], historyCopy[i]
+ })
+ shuffles[i] = historyCopy
+ }
+ return shuffles
+}
+
+func validateShuffles(b *testing.B, lg *zap.Logger, shuffles [][]porcupine.Operation, duration time.Duration) {
+ keys := model.ModelKeys(shuffles[0])
+ for i := 0; i < len(shuffles); i++ {
+ result := validateLinearizableOperationsAndVisualize(lg, keys, shuffles[i], duration)
+ if err := result.Error(); err != nil {
+ b.Fatalf("Not linearizable: %v", err)
+ }
+ }
+}
diff --git a/tests/robustness/validate/patch_history.go b/tests/robustness/validate/patch_history.go
index 5ec3bd2ae2ed..98c8711d5af9 100644
--- a/tests/robustness/validate/patch_history.go
+++ b/tests/robustness/validate/patch_history.go
@@ -16,6 +16,7 @@ package validate
import (
"fmt"
+ "math"
"github.com/anishathalye/porcupine"
@@ -23,32 +24,60 @@ import (
"go.etcd.io/etcd/tests/v3/robustness/report"
)
-func patchLinearizableOperations(reports []report.ClientReport, persistedRequests []model.EtcdRequest) []porcupine.Operation {
- allOperations := relevantOperations(reports)
- putRevision := putRevision(reports)
- putReturnTime := putReturnTime(allOperations, reports, persistedRequests)
- clientPutCount := countClientPuts(reports)
- persistedPutCount := countPersistedPuts(persistedRequests)
- return patchOperations(allOperations, putRevision, putReturnTime, clientPutCount, persistedPutCount)
+type patchArgs struct {
+ returnTime int64
+ clientCount int64
+ persistedCount int64
+ revision int64
}
-func relevantOperations(reports []report.ClientReport) []porcupine.Operation {
- var ops []porcupine.Operation
- for _, r := range reports {
- for _, op := range r.KeyValue {
- request := op.Input.(model.EtcdRequest)
- resp := op.Output.(model.MaybeEtcdResponse)
- // Remove failed read requests as they are not relevant for linearization.
- if resp.Error == "" || !request.IsRead() {
- ops = append(ops, op)
- }
+func patchLinearizableOperations(operations []porcupine.Operation, reports []report.ClientReport, persistedRequests []model.EtcdRequest) []porcupine.Operation {
+ putRevision := watchRevisions(reports)
+ persistedPutCount := countPersistedPuts(persistedRequests)
+ clientPutCount := countClientPuts(reports)
+
+ persistedDeleteCount := countPersistedDeletes(persistedRequests)
+ clientDeleteCount := countClientDeletes(reports)
+
+ persistedCompactCount := countPersistedCompacts(persistedRequests)
+ clientCompactCount := countClientCompacts(reports)
+
+ putReturnTime, delReturnTime, compactReturnTime := uniqueOperationReturnTime(operations, persistedRequests, clientPutCount, clientDeleteCount, clientCompactCount)
+
+ putArgs := make(map[model.PutOptions]patchArgs)
+ for opts, c := range clientPutCount {
+ putArgs[opts] = patchArgs{
+ clientCount: c,
+ persistedCount: persistedPutCount[opts],
+ returnTime: putReturnTime[opts],
+ revision: putRevision[opts],
+ }
+ }
+ delArgs := make(map[model.DeleteOptions]patchArgs)
+ for opts, c := range clientDeleteCount {
+ delArgs[opts] = patchArgs{
+ clientCount: c,
+ persistedCount: persistedDeleteCount[opts],
+ returnTime: delReturnTime[opts],
+ }
+ }
+ compactArgs := make(map[model.CompactRequest]patchArgs)
+ for opts, c := range clientCompactCount {
+ compactArgs[opts] = patchArgs{
+ clientCount: c,
+ persistedCount: persistedCompactCount[opts],
+ returnTime: compactReturnTime[opts],
}
}
- return ops
+
+ return patchOperations(
+ operations, putArgs, delArgs, compactArgs,
+ )
}
-func putRevision(reports []report.ClientReport) map[keyValue]int64 {
- requestRevision := map[keyValue]int64{}
+func watchRevisions(reports []report.ClientReport) map[model.PutOptions]int64 {
+ putRevisions := map[model.PutOptions]int64{}
+
for _, client := range reports {
for _, watch := range client.Watch {
for _, resp := range watch.Responses {
@@ -56,9 +85,10 @@ func putRevision(reports []report.ClientReport) map[keyValue]int64 {
switch event.Type {
case model.RangeOperation:
case model.PutOperation:
- kv := keyValue{Key: event.Key, Value: event.Value}
- requestRevision[kv] = event.Revision
+ kv := model.PutOptions{Key: event.Key, Value: event.Value}
+ putRevisions[kv] = event.Revision
case model.DeleteOperation:
+ // Delete events are also created by leaseRevoke request.
default:
panic(fmt.Sprintf("unknown event type %q", event.Type))
}
@@ -66,15 +96,36 @@ func putRevision(reports []report.ClientReport) map[keyValue]int64 {
}
}
}
- return requestRevision
+ return putRevisions
}
-func patchOperations(operations []porcupine.Operation, watchRevision, putReturnTime, clientPutCount, persistedPutCount map[keyValue]int64) []porcupine.Operation {
+func patchOperations(
+ operations []porcupine.Operation,
+ putArgs map[model.PutOptions]patchArgs,
+ delArgs map[model.DeleteOptions]patchArgs,
+ compactArgs map[model.CompactRequest]patchArgs,
+) []porcupine.Operation {
newOperations := make([]porcupine.Operation, 0, len(operations))
for _, op := range operations {
request := op.Input.(model.EtcdRequest)
resp := op.Output.(model.MaybeEtcdResponse)
+
+ if request.Type == model.Compact {
+ kv := model.CompactRequest{Revision: request.Compact.Revision}
+ if arg, ok := compactArgs[kv]; ok && arg.clientCount == 1 {
+ if arg.persistedCount == 0 && resp.Error != "" {
+ // the failed compact should be dropped
+ continue
+ }
+ if arg.returnTime > 0 {
+ op.Return = min(op.Return, arg.returnTime)
+ }
+ }
+ newOperations = append(newOperations, op)
+ continue
+ }
+
if resp.Error == "" || request.Type != model.Txn {
// Cannot patch those requests.
newOperations = append(newOperations, op)
@@ -82,29 +133,50 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
}
var txnRevision int64
var persisted bool
- for _, etcdOp := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
+ for _, etcdOp := range request.Txn.AllOperations() {
switch etcdOp.Type {
case model.PutOperation:
- kv := keyValue{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
- if _, ok := persistedPutCount[kv]; ok {
+ kv := model.PutOptions{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
+ arg, ok := putArgs[kv]
+ if !ok {
+ continue
+ }
+ if arg.persistedCount > 0 {
persisted = true
}
- if count := clientPutCount[kv]; count != 1 {
+ if arg.clientCount != 1 {
continue
}
- if revision, ok := watchRevision[kv]; ok {
- txnRevision = revision
+ if arg.revision > 0 {
+ txnRevision = arg.revision
}
- if returnTime, ok := putReturnTime[kv]; ok {
- op.Return = min(op.Return, returnTime)
+ if arg.returnTime > 0 {
+ op.Return = min(op.Return, arg.returnTime)
}
case model.DeleteOperation:
+ kv := model.DeleteOptions{Key: etcdOp.Delete.Key}
+ arg, ok := delArgs[kv]
+ if !ok {
+ continue
+ }
+ if arg.persistedCount > 0 {
+ persisted = true
+ }
+ if arg.clientCount != 1 {
+ continue
+ }
+ if arg.revision > 0 {
+ txnRevision = arg.revision
+ }
+ if arg.returnTime > 0 {
+ op.Return = min(op.Return, arg.returnTime)
+ }
case model.RangeOperation:
default:
panic(fmt.Sprintf("unknown operation type %q", etcdOp.Type))
}
}
- if isUniqueTxn(request.Txn, clientPutCount) {
+ if isUniqueTxn(request.Txn, putArgs, delArgs) {
if !persisted {
// Remove non persisted operations
continue
@@ -121,12 +193,12 @@ func patchOperations(operations []porcupine.Operation, watchRevision, putReturnT
return newOperations
}
-func isUniqueTxn(request *model.TxnRequest, clientRequestCount map[keyValue]int64) bool {
- return isUniqueOps(request.OperationsOnSuccess, clientRequestCount) && isUniqueOps(request.OperationsOnFailure, clientRequestCount)
+func isUniqueTxn(request *model.TxnRequest, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
+ return isUniqueOps(request.OperationsOnSuccess, putArgs, delArgs) && isUniqueOps(request.OperationsOnFailure, putArgs, delArgs)
}
-func isUniqueOps(ops []model.EtcdOperation, clientRequestCount map[keyValue]int64) bool {
- return hasUniqueWriteOperation(ops, clientRequestCount) || !hasWriteOperation(ops)
+func isUniqueOps(ops []model.EtcdOperation, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
+ return hasUniqueWriteOperation(ops, putArgs, delArgs) || !hasWriteOperation(ops)
}
func hasWriteOperation(ops []model.EtcdOperation) bool {
@@ -138,15 +210,19 @@ func hasWriteOperation(ops []model.EtcdOperation) bool {
return false
}
-func hasUniqueWriteOperation(ops []model.EtcdOperation, clientRequestCount map[keyValue]int64) bool {
+func hasUniqueWriteOperation(ops []model.EtcdOperation, putArgs map[model.PutOptions]patchArgs, delArgs map[model.DeleteOptions]patchArgs) bool {
for _, operation := range ops {
switch operation.Type {
case model.PutOperation:
- kv := keyValue{Key: operation.Put.Key, Value: operation.Put.Value}
- if count := clientRequestCount[kv]; count == 1 {
+ kv := model.PutOptions{Key: operation.Put.Key, Value: operation.Put.Value}
+ if arg, ok := putArgs[kv]; ok && arg.clientCount == 1 {
return true
}
case model.DeleteOperation:
+ kv := model.DeleteOptions{Key: operation.Delete.Key}
+ if arg, ok := delArgs[kv]; ok && arg.clientCount == 1 {
+ return true
+ }
case model.RangeOperation:
default:
panic(fmt.Sprintf("unknown operation type %q", operation.Type))
@@ -155,27 +231,56 @@ func hasUniqueWriteOperation(ops []model.EtcdOperation, clientRequestCount map[k
return false
}
-func putReturnTime(allOperations []porcupine.Operation, reports []report.ClientReport, persistedRequests []model.EtcdRequest) map[keyValue]int64 {
- earliestReturnTime := map[keyValue]int64{}
+func uniqueOperationReturnTime(
+ allOperations []porcupine.Operation,
+ persistedRequests []model.EtcdRequest,
+ clientPutCount map[model.PutOptions]int64,
+ clientDeleteCount map[model.DeleteOptions]int64,
+ clientCompactCount map[model.CompactRequest]int64,
+) (
+ map[model.PutOptions]int64,
+ map[model.DeleteOptions]int64,
+ map[model.CompactRequest]int64,
+) {
+ putTimes := map[model.PutOptions]int64{}
+ delTimes := map[model.DeleteOptions]int64{}
+ compactTimes := map[model.CompactRequest]int64{}
var lastReturnTime int64
for _, op := range allOperations {
request := op.Input.(model.EtcdRequest)
switch request.Type {
case model.Txn:
- for _, etcdOp := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
- if etcdOp.Type != model.PutOperation {
- continue
- }
- kv := keyValue{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
- if returnTime, ok := earliestReturnTime[kv]; !ok || returnTime > op.Return {
- earliestReturnTime[kv] = op.Return
+ for _, etcdOp := range request.Txn.AllOperations() {
+ switch etcdOp.Type {
+ case model.PutOperation:
+ kv := model.PutOptions{Key: etcdOp.Put.Key, Value: etcdOp.Put.Value}
+ if clientPutCount[kv] > 1 {
+ continue
+ }
+ if returnTime, ok := putTimes[kv]; !ok || returnTime > op.Return {
+ putTimes[kv] = op.Return
+ }
+ case model.DeleteOperation:
+ kv := model.DeleteOptions{Key: etcdOp.Delete.Key}
+ if clientDeleteCount[kv] > 1 {
+ continue
+ }
+ if returnTime, ok := delTimes[kv]; !ok || returnTime > op.Return {
+ delTimes[kv] = op.Return
+ }
}
- earliestReturnTime[kv] = op.Return
}
case model.Range:
case model.LeaseGrant:
case model.LeaseRevoke:
case model.Compact:
+ if clientCompactCount[*request.Compact] > 1 {
+ continue
+ }
+ if returnTime, ok := compactTimes[*request.Compact]; !ok || returnTime > op.Return {
+ compactTimes[*request.Compact] = op.Return
+ }
+ case model.Defragment:
default:
panic(fmt.Sprintf("Unknown request type: %q", request.Type))
}
@@ -184,54 +289,57 @@ func putReturnTime(allOperations []porcupine.Operation, reports []report.ClientR
}
}
- for _, client := range reports {
- for _, watch := range client.Watch {
- for _, resp := range watch.Responses {
- for _, event := range resp.Events {
- switch event.Type {
- case model.RangeOperation:
- case model.PutOperation:
- kv := keyValue{Key: event.Key, Value: event.Value}
- if t, ok := earliestReturnTime[kv]; !ok || t > resp.Time.Nanoseconds() {
- earliestReturnTime[kv] = resp.Time.Nanoseconds()
- }
- case model.DeleteOperation:
- default:
- panic(fmt.Sprintf("unknown event type %q", event.Type))
- }
- }
- }
- }
- }
-
for i := len(persistedRequests) - 1; i >= 0; i-- {
request := persistedRequests[i]
switch request.Type {
case model.Txn:
- lastReturnTime--
- for _, op := range request.Txn.OperationsOnSuccess {
- if op.Type != model.PutOperation {
- continue
- }
- kv := keyValue{Key: op.Put.Key, Value: op.Put.Value}
- returnTime, ok := earliestReturnTime[kv]
- if ok {
- lastReturnTime = min(returnTime, lastReturnTime)
- earliestReturnTime[kv] = lastReturnTime
+ if lastReturnTime != math.MaxInt64 {
+ lastReturnTime--
+ }
+ for _, op := range request.Txn.AllOperations() {
+ switch op.Type {
+ case model.PutOperation:
+ kv := model.PutOptions{Key: op.Put.Key, Value: op.Put.Value}
+ if clientPutCount[kv] > 1 {
+ continue
+ }
+ if returnTime, ok := putTimes[kv]; ok {
+ lastReturnTime = min(returnTime, lastReturnTime)
+ putTimes[kv] = lastReturnTime
+ }
+ case model.DeleteOperation:
+ kv := model.DeleteOptions{Key: op.Delete.Key}
+ if clientDeleteCount[kv] > 1 {
+ continue
+ }
+ if returnTime, ok := delTimes[kv]; ok {
+ lastReturnTime = min(returnTime, lastReturnTime)
+ delTimes[kv] = lastReturnTime
+ }
}
}
case model.LeaseGrant:
case model.LeaseRevoke:
case model.Compact:
+ if lastReturnTime != math.MaxInt64 {
+ lastReturnTime--
+ }
+ if clientCompactCount[*request.Compact] > 1 {
+ continue
+ }
+ if returnTime, ok := compactTimes[*request.Compact]; ok {
+ lastReturnTime = min(returnTime, lastReturnTime)
+ compactTimes[*request.Compact] = lastReturnTime
+ }
default:
panic(fmt.Sprintf("Unknown request type: %q", request.Type))
}
}
- return earliestReturnTime
+ return putTimes, delTimes, compactTimes
}
-func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
- counter := map[keyValue]int64{}
+func countClientPuts(reports []report.ClientReport) map[model.PutOptions]int64 {
+ counter := map[model.PutOptions]int64{}
for _, client := range reports {
for _, op := range client.KeyValue {
request := op.Input.(model.EtcdRequest)
@@ -241,21 +349,21 @@ func countClientPuts(reports []report.ClientReport) map[keyValue]int64 {
return counter
}
-func countPersistedPuts(requests []model.EtcdRequest) map[keyValue]int64 {
- counter := map[keyValue]int64{}
+func countPersistedPuts(requests []model.EtcdRequest) map[model.PutOptions]int64 {
+ counter := map[model.PutOptions]int64{}
for _, request := range requests {
countPuts(counter, request)
}
return counter
}
-func countPuts(counter map[keyValue]int64, request model.EtcdRequest) {
+func countPuts(counter map[model.PutOptions]int64, request model.EtcdRequest) {
switch request.Type {
case model.Txn:
- for _, operation := range append(request.Txn.OperationsOnSuccess, request.Txn.OperationsOnFailure...) {
+ for _, operation := range request.Txn.AllOperations() {
switch operation.Type {
case model.PutOperation:
- kv := keyValue{Key: operation.Put.Key, Value: operation.Put.Value}
+ kv := model.PutOptions{Key: operation.Put.Key, Value: operation.Put.Value}
counter[kv]++
case model.DeleteOperation:
case model.RangeOperation:
@@ -273,7 +381,57 @@ func countPuts(counter map[keyValue]int64, request model.EtcdRequest) {
}
}
-type keyValue struct {
- Key string
- Value model.ValueOrHash
+func countClientDeletes(reports []report.ClientReport) map[model.DeleteOptions]int64 {
+ counter := map[model.DeleteOptions]int64{}
+ for _, client := range reports {
+ for _, op := range client.KeyValue {
+ request := op.Input.(model.EtcdRequest)
+ countDeletes(counter, request)
+ }
+ }
+ return counter
+}
+
+func countPersistedDeletes(requests []model.EtcdRequest) map[model.DeleteOptions]int64 {
+ counter := map[model.DeleteOptions]int64{}
+ for _, req := range requests {
+ countDeletes(counter, req)
+ }
+ return counter
+}
+
+func countDeletes(counter map[model.DeleteOptions]int64, request model.EtcdRequest) {
+ if request.Type != model.Txn {
+ return
+ }
+ for _, operation := range request.Txn.AllOperations() {
+ if operation.Type == model.DeleteOperation {
+ counter[operation.Delete]++
+ }
+ }
+}
+
+func countClientCompacts(reports []report.ClientReport) map[model.CompactRequest]int64 {
+ counter := map[model.CompactRequest]int64{}
+ for _, client := range reports {
+ for _, op := range client.KeyValue {
+ request := op.Input.(model.EtcdRequest)
+ countCompacts(counter, request)
+ }
+ }
+ return counter
+}
+
+func countPersistedCompacts(requests []model.EtcdRequest) map[model.CompactRequest]int64 {
+ counter := map[model.CompactRequest]int64{}
+ for _, req := range requests {
+ countCompacts(counter, req)
+ }
+ return counter
+}
+
+func countCompacts(counter map[model.CompactRequest]int64, request model.EtcdRequest) {
+ if request.Type == model.Compact {
+ counter[*request.Compact]++
+ }
}
diff --git a/tests/robustness/validate/patch_history_test.go b/tests/robustness/validate/patch_history_test.go
index bb104b0125fe..5e9b0dc84f1a 100644
--- a/tests/robustness/validate/patch_history_test.go
+++ b/tests/robustness/validate/patch_history_test.go
@@ -17,6 +17,7 @@ package validate
import (
"errors"
+ "math"
"testing"
"time"
@@ -30,7 +31,7 @@ import (
"go.etcd.io/etcd/tests/v3/robustness/report"
)
-const infinite = 1000000000
+const infinite = math.MaxInt64
func TestPatchHistory(t *testing.T) {
for _, tc := range []struct {
@@ -43,7 +44,7 @@ func TestPatchHistory(t *testing.T) {
{
name: "successful range remains",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendRange("key", "", 0, 0, 100, 200, &clientv3.GetResponse{}, nil)
+ h.AppendRange("key", "", 0, 0, false, 100, 200, &clientv3.GetResponse{}, nil)
},
expectedRemainingOperations: []porcupine.Operation{
{Return: 200, Output: rangeResponse(0)},
@@ -58,25 +59,25 @@ func TestPatchHistory(t *testing.T) {
putRequest("key", "value"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: 200, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed put remains if there is a matching event, return time untouched",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPut("key", "value", 100, infinite, nil, errors.New("failed"))
+ h.AppendPut("key", "value", 100, 200, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key", "value"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 99, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
name: "failed put remains if there is a matching event, uniqueness allows for return time to be based on next persisted request",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPut("key1", "value", 100, infinite, nil, errors.New("failed"))
+ h.AppendPut("key1", "value", 100, 200, nil, errors.New("failed"))
h.AppendPut("key2", "value", 300, 400, &clientv3.PutResponse{}, nil)
},
persistedRequest: []model.EtcdRequest{
@@ -85,20 +86,20 @@ func TestPatchHistory(t *testing.T) {
},
expectedRemainingOperations: []porcupine.Operation{
{Return: 399, Output: model.MaybeEtcdResponse{Persisted: true}},
- {Return: 400, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
- name: "failed put remains if there is a matching persisted request, uniqueness allows for revision and return time to be based on watch",
+ name: "failed put remains if there is a matching persisted request, uniqueness allows for revision to be based on watch",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPut("key", "value", 100, infinite, nil, errors.New("failed"))
+ h.AppendPut("key", "value", 100, 200, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key", "value"),
},
watchOperations: watchResponse(300, putEvent("key", "value", 2)),
expectedRemainingOperations: []porcupine.Operation{
- {Return: 300, Output: model.MaybeEtcdResponse{Persisted: true, PersistedRevision: 2}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true, PersistedRevision: 2}},
},
},
{
@@ -113,52 +114,52 @@ func TestPatchHistory(t *testing.T) {
},
watchOperations: watchResponse(3, putEvent("key", "value", 2), putEvent("key", "value", 3)),
expectedRemainingOperations: []porcupine.Operation{
- {Return: 1000000004, Output: model.MaybeEtcdResponse{Error: "failed"}},
- {Return: 4, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 4, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed put is dropped if event has different key",
historyFunc: func(h *model.AppendableHistory) {
h.AppendPut("key2", "value", 100, 200, &clientv3.PutResponse{}, nil)
- h.AppendPut("key1", "value", 300, infinite, nil, errors.New("failed"))
+ h.AppendPut("key1", "value", 300, 400, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key2", "value"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: 200, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed put is dropped if event has different value",
historyFunc: func(h *model.AppendableHistory) {
h.AppendPut("key", "value2", 100, 200, &clientv3.PutResponse{}, nil)
- h.AppendPut("key", "value1", 300, infinite, nil, errors.New("failed"))
+ h.AppendPut("key", "value1", 300, 400, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key", "value2"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: 200, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed put with lease remains if there is a matching event, return time untouched",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPutWithLease("key", "value", 123, 100, infinite, nil, errors.New("failed"))
+ h.AppendPutWithLease("key", "value", 123, 100, 200, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequestWithLease("key", "value", 123),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 99, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
name: "failed put with lease remains if there is a matching event, uniqueness allows return time to be based on next persisted request",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPutWithLease("key1", "value", 123, 100, infinite, nil, errors.New("failed"))
+ h.AppendPutWithLease("key1", "value", 123, 100, 200, nil, errors.New("failed"))
h.AppendPutWithLease("key2", "value", 234, 300, 400, &clientv3.PutResponse{}, nil)
},
persistedRequest: []model.EtcdRequest{
@@ -167,11 +168,11 @@ func TestPatchHistory(t *testing.T) {
},
expectedRemainingOperations: []porcupine.Operation{
{Return: 399, Output: model.MaybeEtcdResponse{Persisted: true}},
- {Return: 400, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
- name: "failed put with lease remains if there is a matching event, uniqueness allows for revision and return time to be based on watch",
+ name: "failed put with lease remains if there is a matching event, uniqueness allows for revision to be based on watch",
historyFunc: func(h *model.AppendableHistory) {
h.AppendPutWithLease("key", "value", 123, 1, 2, nil, errors.New("failed"))
},
@@ -180,7 +181,7 @@ func TestPatchHistory(t *testing.T) {
},
watchOperations: watchResponse(3, putEvent("key", "value", 2)),
expectedRemainingOperations: []porcupine.Operation{
- {Return: 3, Output: model.MaybeEtcdResponse{Persisted: true, PersistedRevision: 2}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true, PersistedRevision: 2}},
},
},
{
@@ -195,21 +196,21 @@ func TestPatchHistory(t *testing.T) {
},
watchOperations: watchResponse(3, putEvent("key", "value", 2), putEvent("key", "value", 3)),
expectedRemainingOperations: []porcupine.Operation{
- {Return: 1000000004, Output: model.MaybeEtcdResponse{Error: "failed"}},
- {Return: 4, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 4, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed put is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPut("key", "value", 100, infinite, nil, errors.New("failed"))
+ h.AppendPut("key", "value", 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed put with lease is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendPutWithLease("key", "value", 123, 100, infinite, nil, errors.New("failed"))
+ h.AppendPutWithLease("key", "value", 123, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
@@ -219,57 +220,110 @@ func TestPatchHistory(t *testing.T) {
h.AppendDelete("key", 100, 200, &clientv3.DeleteResponse{}, nil)
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: 200, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
- name: "failed delete remains, time untouched regardless of persisted event and watch",
+ name: "failed delete with lease is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendDelete("key", 100, infinite, nil, errors.New("failed"))
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
+ },
+ expectedRemainingOperations: []porcupine.Operation{},
+ },
+ {
+ name: "failed delete remains, if there is a persisted request",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
+ },
+ },
+ {
+ name: "failed delete remains, if there is a persisted request, revision is not patched based on watch due to leaseRevoke also triggering delete watch events",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
+ },
+ watchOperations: watchResponse(3, deleteEvent("key", 2)),
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
+ },
+ },
+ {
+ name: "failed delete remains, if there is a matching persisted request, uniqueness of this operation and following operation allows patching based on following operation",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
h.AppendPut("key", "value", 300, 400, &clientv3.PutResponse{}, nil)
},
persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
putRequest("key", "value"),
},
- watchOperations: watchResponse(3, deleteEvent("key", 2)),
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 400, Output: model.MaybeEtcdResponse{Error: "failed"}},
- {Return: 400, Output: putResponse(model.EtcdOperationResult{})},
+ {Return: 399, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed delete remains if there is a matching persisted request, lack of uniqueness of this operation prevents patching based on following operation",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
+ h.AppendPut("key", "value", 300, 400, &clientv3.PutResponse{}, nil)
+ h.AppendDelete("key", 500, 600, &clientv3.DeleteResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
+ putRequest("key", "value"),
+ deleteRequest("key"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed empty txn is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn put is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn put remains if there is a matching event",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key", "value"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 99, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
name: "failed txn delete remains",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpDelete("key")}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpDelete("key")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 100, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
@@ -277,91 +331,235 @@ func TestPatchHistory(t *testing.T) {
historyFunc: func(h *model.AppendableHistory) {
h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{clientv3.OpDelete("key")}, 100, 200, &clientv3.TxnResponse{Succeeded: true}, nil)
},
+ persistedRequest: []model.EtcdRequest{
+ putRequest("key", "value"),
+ },
expectedRemainingOperations: []porcupine.Operation{
- {Return: 200, Output: putResponse()},
+ // It's successful, so it remains as-is with its original response
+ {Return: 200, Output: txnResponse(0)},
},
},
{
- name: "failed txn put/delete remains",
+ name: "failed txn empty/delete is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{clientv3.OpDelete("key")}, 100, infinite, nil, errors.New("failed"))
- },
- expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 100, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpDelete("key")}, 100, 200, nil, errors.New("failed"))
},
+ expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn delete/put remains",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpDelete("key")}, []clientv3.Op{clientv3.OpPut("key", "value")}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpDelete("key")}, []clientv3.Op{clientv3.OpPut("key", "value")}, 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 100, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
name: "failed txn empty/put is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpPut("key", "value")}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpPut("key", "value")}, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn empty/put remains if there is a matching event",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
},
persistedRequest: []model.EtcdRequest{
putRequest("key", "value"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 99, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
+ },
+ },
+ {
+ name: "failed put remains if there is a matching persisted request, uniqueness of this operation and following operation allows patching based on following operation",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendPut("key1", "value1", 300, 400, nil, errors.New("failed"))
+ h.AppendPut("key2", "value2", 500, 600, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ putRequest("key1", "value1"),
+ putRequest("key2", "value2"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: 599, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed put remains if there is a matching persisted request, lack of uniqueness of this operation prevents patching based on following operation",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendPut("key1", "value1", 100, 200, &clientv3.PutResponse{}, nil)
+ h.AppendPut("key1", "value1", 300, 400, nil, errors.New("failed"))
+ h.AppendPut("key2", "value2", 500, 600, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ putRequest("key1", "value1"),
+ putRequest("key1", "value1"),
+ putRequest("key2", "value2"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed put remains if there is a matching persisted request, lack of uniqueness of following operation prevents patching based on following operation",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendPut("key2", "value2", 100, 200, &clientv3.PutResponse{}, nil)
+ h.AppendPut("key1", "value1", 300, 400, nil, errors.New("failed"))
+ h.AppendPut("key2", "value2", 500, 600, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ putRequest("key2", "value2"),
+ putRequest("key1", "value1"),
+ putRequest("key2", "value2"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: 200, Output: txnResponse(0, model.EtcdOperationResult{})},
+ // TODO: We can infer that failed operation finished before last operation matching following.
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
},
},
{
name: "failed txn empty/delete remains",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpDelete("key")}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpDelete("key")}, 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
},
expectedRemainingOperations: []porcupine.Operation{
- {Return: infinite + 100, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: infinite, Output: model.MaybeEtcdResponse{Persisted: true}},
},
},
{
name: "failed txn put&delete is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, []clientv3.Op{}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn empty/put&delete is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{}, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, 100, 200, nil, errors.New("failed"))
},
expectedRemainingOperations: []porcupine.Operation{},
},
{
name: "failed txn put&delete/put&delete is dropped",
historyFunc: func(h *model.AppendableHistory) {
- h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, []clientv3.Op{clientv3.OpPut("key", "value2"), clientv3.OpDelete("key")}, 100, infinite, nil, errors.New("failed"))
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpPut("key", "value1"), clientv3.OpDelete("key")}, []clientv3.Op{clientv3.OpPut("key", "value2"), clientv3.OpDelete("key")}, 100, 200, nil, errors.New("failed"))
+ },
+ expectedRemainingOperations: []porcupine.Operation{},
+ },
+ {
+ name: "failed delete remains, time untouched due to non-uniqueness",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendDelete("key", 100, 200, nil, errors.New("failed"))
+ h.AppendDelete("key", 300, 400, &clientv3.DeleteResponse{}, nil)
+ h.AppendPut("key", "value", 500, 600, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ deleteRequest("key"),
+ deleteRequest("key"),
+ putRequest("key", "value"),
+ },
+ watchOperations: watchResponse(250, deleteEvent("key", 2)),
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed txn delete is dropped when not persisted",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendTxn(nil, []clientv3.Op{clientv3.OpDelete("key")}, []clientv3.Op{}, 100, 200, nil, errors.New("failed"))
+ },
+ expectedRemainingOperations: []porcupine.Operation{},
+ },
+ {
+ name: "successful compact with unique revision keeps original return time",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendCompact(5, 100, 200, &clientv3.CompactResponse{}, nil)
+ h.AppendPut("key", "value", 300, 400, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ compactRequest(5),
+ putRequest("key", "value"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: 200, Output: model.MaybeEtcdResponse{EtcdResponse: model.EtcdResponse{Revision: -1, Compact: &model.CompactResponse{}}}},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed compact with unique revision is patched with return time",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendCompact(5, 100, 200, nil, errors.New("failed"))
+ h.AppendPut("key", "value", 300, 400, &clientv3.PutResponse{}, nil)
+ },
+ persistedRequest: []model.EtcdRequest{
+ compactRequest(5),
+ putRequest("key", "value"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: 399, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 400, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed compact with non-unique revision remains unchanged",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendCompact(5, 100, 200, nil, errors.New("failed"))
+ h.AppendCompact(5, 300, 400, &clientv3.CompactResponse{}, nil)
+ h.AppendPut("key", "value", 500, 600, &clientv3.PutResponse{}, nil)
},
+ persistedRequest: []model.EtcdRequest{
+ compactRequest(5),
+ compactRequest(5),
+ putRequest("key", "value"),
+ },
+ expectedRemainingOperations: []porcupine.Operation{
+ {Return: infinite, Output: model.MaybeEtcdResponse{Error: "failed"}},
+ {Return: 400, Output: model.MaybeEtcdResponse{EtcdResponse: model.EtcdResponse{Revision: -1, Compact: &model.CompactResponse{}}}},
+ {Return: 600, Output: txnResponse(0, model.EtcdOperationResult{})},
+ },
+ },
+ {
+ name: "failed compact with unique revision is dropped when not persisted",
+ historyFunc: func(h *model.AppendableHistory) {
+ h.AppendCompact(5, 100, 200, nil, errors.New("failed"))
+ },
+ persistedRequest: []model.EtcdRequest{},
expectedRemainingOperations: []porcupine.Operation{},
},
} {
t.Run(tc.name, func(t *testing.T) {
history := model.NewAppendableHistory(identity.NewIDProvider())
tc.historyFunc(history)
- operations := patchLinearizableOperations([]report.ClientReport{
+ reports := []report.ClientReport{
{
ClientID: 0,
KeyValue: history.History.Operations(),
Watch: tc.watchOperations,
},
- }, tc.persistedRequest)
- if diff := cmp.Diff(tc.expectedRemainingOperations, operations,
+ }
+ operations, _, _ := prepareAndCategorizeOperations(reports)
+ patched := patchLinearizableOperations(operations, reports, tc.persistedRequest)
+ if diff := cmp.Diff(tc.expectedRemainingOperations, patched,
cmpopts.EquateEmpty(),
- cmpopts.IgnoreFields(porcupine.Operation{}, "Input", "Call", "ClientId"),
+ cmpopts.IgnoreFields(porcupine.Operation{}, "Input", "Call", "ClientId", "Metadata"),
); diff != "" {
t.Errorf("Response didn't match expected, diff:\n%s", diff)
}
@@ -369,8 +567,8 @@ func TestPatchHistory(t *testing.T) {
}
}
-func putResponse(result ...model.EtcdOperationResult) model.MaybeEtcdResponse {
- return model.MaybeEtcdResponse{EtcdResponse: model.EtcdResponse{Txn: &model.TxnResponse{Results: result}}}
+func txnResponse(rev int64, result ...model.EtcdOperationResult) model.MaybeEtcdResponse {
+ return model.MaybeEtcdResponse{EtcdResponse: model.EtcdResponse{Revision: rev, Txn: &model.TxnResponse{Results: result}}}
}
func watchResponse(responseTime int64, events ...model.WatchEvent) []model.WatchOperation {
@@ -410,3 +608,12 @@ func deleteEvent(key string, revision int64) model.WatchEvent {
},
}
}
+
+func compactRequest(revision int64) model.EtcdRequest {
+ return model.EtcdRequest{
+ Type: model.Compact,
+ Compact: &model.CompactRequest{
+ Revision: revision,
+ },
+ }
+}
diff --git a/tests/robustness/validate/result.go b/tests/robustness/validate/result.go
new file mode 100644
index 000000000000..aac8d36a9bfe
--- /dev/null
+++ b/tests/robustness/validate/result.go
@@ -0,0 +1,125 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 validate
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/anishathalye/porcupine"
+ "go.uber.org/zap"
+)
+
+type RobustnessResult struct {
+ Assumptions Result
+ Linearization LinearizationResult
+ Watch Result
+ Serializable Result
+}
+
+type Result struct {
+ Status ResultStatus
+ Message string
+}
+
+type ResultStatus string
+
+var (
+ Unknown ResultStatus
+ Success ResultStatus = "Success"
+ Failure ResultStatus = "Failure"
+ Timeout ResultStatus = "Timeout"
+ // DeadlineExceeded is a workaround for Porcupine not always enforcing its timeout. It does not support visualization.
+ DeadlineExceeded ResultStatus = "DeadlineExceeded"
+)
+
+func (r RobustnessResult) Error() error {
+ if err := r.Assumptions.Error(); err != nil {
+ return fmt.Errorf("assumptions: %w", err)
+ }
+ if err := r.Linearization.Error(); err != nil {
+ return fmt.Errorf("linearization: %w", err)
+ }
+ if err := r.Watch.Error(); err != nil {
+ return fmt.Errorf("watch: %w", err)
+ }
+ if err := r.Serializable.Error(); err != nil {
+ return fmt.Errorf("serializable: %w", err)
+ }
+ return nil
+}
+
+func ResultFromError(err error) Result {
+ if err != nil {
+ return Result{
+ Status: Failure,
+ Message: err.Error(),
+ }
+ }
+ return Result{
+ Status: Success,
+ }
+}
+
+func (r Result) Error() error {
+ switch r.Status {
+ case Success, Unknown:
+ return nil
+ default:
+ return errors.New(r.String())
+ }
+}
+
+func (r Result) String() string {
+ if r.Message != "" {
+ return fmt.Sprintf("%s: %s", r.Status, r.Message)
+ }
+ return string(r.Status)
+}
+
+type LinearizationResult struct {
+ Info porcupine.LinearizationInfo
+ Model porcupine.Model
+ Result
+}
+
+func (r *LinearizationResult) Visualize(lg *zap.Logger, path string) error {
+ err := r.Error()
+ if err != nil {
+ lg.Info("Skipping linearization visualization", zap.Error(err))
+ return nil
+ }
+
+ lg.Info("Saving visualization", zap.String("path", path))
+ err = porcupine.VisualizePath(r.Model, r.Info, path)
+ if err != nil {
+ return fmt.Errorf("failed to visualize, err: %w", err)
+ }
+ return nil
+}
+
+func (r *LinearizationResult) AddToVisualization(serializable []porcupine.Operation) {
+ annotations := []porcupine.Annotation{}
+ for _, op := range serializable {
+ annotations = append(annotations, porcupine.Annotation{
+ ClientId: op.ClientId,
+ Start: op.Call,
+ End: op.Return,
+ Description: r.Model.DescribeOperation(op.Input, op.Output),
+ Details: r.Model.DescribeOperationMetadata(op.Metadata),
+ })
+ }
+ r.Info.AddAnnotations(annotations)
+}
diff --git a/tests/robustness/validate/validate.go b/tests/robustness/validate/validate.go
index 5918ec0df83c..0a8b370c2abb 100644
--- a/tests/robustness/validate/validate.go
+++ b/tests/robustness/validate/validate.go
@@ -15,59 +15,108 @@
package validate
import (
- "encoding/json"
+ "errors"
"fmt"
- "testing"
+ "math"
"time"
"github.com/anishathalye/porcupine"
- "github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.etcd.io/etcd/tests/v3/robustness/model"
"go.etcd.io/etcd/tests/v3/robustness/report"
)
-// ValidateAndReturnVisualize returns visualize as porcupine.linearizationInfo used to generate visualization is private.
-func ValidateAndReturnVisualize(t *testing.T, lg *zap.Logger, cfg Config, reports []report.ClientReport, persistedRequests []model.EtcdRequest, timeout time.Duration) (visualize func(basepath string) error) {
- err := checkValidationAssumptions(reports, persistedRequests)
- require.NoErrorf(t, err, "Broken validation assumptions")
- linearizableOperations := patchLinearizableOperations(reports, persistedRequests)
- serializableOperations := filterSerializableOperations(reports)
+var ErrNotEmptyDatabase = errors.New("non empty database at start, required by model used for linearizability validation")
- linearizable, visualize := validateLinearizableOperationsAndVisualize(lg, linearizableOperations, timeout)
- if linearizable != porcupine.Ok {
- t.Error("Failed linearization, skipping further validation")
- return visualize
+func ValidateAndReturnVisualize(lg *zap.Logger, cfg Config, reports []report.ClientReport, persistedRequests []model.EtcdRequest, timeout time.Duration) (result RobustnessResult) {
+ result.Assumptions = ResultFromError(checkValidationAssumptions(reports))
+ if result.Assumptions.Error() != nil {
+ return result
}
- // TODO: Use requests from linearization for replay.
- replay := model.NewReplay(persistedRequests)
-
- err = validateWatch(lg, cfg, reports, replay)
- if err != nil {
- t.Errorf("Failed validating watch history, err: %s", err)
+ linearizableOperations, serializableOperations, operationsForVisualization := prepareAndCategorizeOperations(reports)
+ // We are passing in the original reports and linearizableOperations with modified return time.
+ // The reason is that linearizableOperations are those dedicated for linearization, which requires them to have returnTime set to infinity as required by pourcupine.
+ // As for the report, the original report is used so the consumer doesn't need to track what patching was done or not.
+ if len(persistedRequests) != 0 {
+ linearizableOperations = patchLinearizableOperations(linearizableOperations, reports, persistedRequests)
}
- err = validateSerializableOperations(lg, serializableOperations, replay)
- if err != nil {
- t.Errorf("Failed validating serializable operations, err: %s", err)
+ keys := model.ModelKeys(linearizableOperations)
+ result.Linearization = validateLinearizableOperationsAndVisualize(lg, keys, linearizableOperations, timeout)
+ result.Linearization.AddToVisualization(operationsForVisualization)
+ // Skip other validations if model is not linearizable, as they are expected to fail too and obfuscate the logs.
+ if result.Linearization.Error() != nil {
+ lg.Info("Skipping other validations as linearization failed")
+ return result
}
- return visualize
+ if len(persistedRequests) == 0 {
+ lg.Info("Skipping other validations as persisted requests were empty")
+ return result
+ }
+ replay := model.NewReplay(persistedRequests)
+ result.Watch = validateWatch(lg, cfg, reports, replay)
+ result.Serializable = validateSerializableOperations(lg, serializableOperations, replay)
+ return result
}
type Config struct {
ExpectRevisionUnique bool
}
-func checkValidationAssumptions(reports []report.ClientReport, persistedRequests []model.EtcdRequest) error {
- err := validateEmptyDatabaseAtStart(reports)
- if err != nil {
- return err
+func prepareAndCategorizeOperations(reports []report.ClientReport) (linearizable, serializable, forVisualization []porcupine.Operation) {
+ for _, report := range reports {
+ for _, op := range report.KeyValue {
+ request := op.Input.(model.EtcdRequest)
+ response := op.Output.(model.MaybeEtcdResponse)
+ if isSerializable(request, response) {
+ serializable = append(serializable, op)
+ }
+ // Operations that will not be linearized need to be added separately to the visualization.
+ if !isLinearizable(request, response) {
+ forVisualization = append(forVisualization, op)
+ continue
+ }
+ // For linearization, we set the return time of failed requests to MaxInt64.
+ // Failed requests can still be persisted, however we don't know when the request has taken effect.
+ if response.Error != "" {
+ op.Return = math.MaxInt64
+ }
+ linearizable = append(linearizable, op)
+ }
}
+ return linearizable, serializable, forVisualization
+}
+
+func isLinearizable(request model.EtcdRequest, response model.MaybeEtcdResponse) bool {
+ // Cannot test response for request without side effect.
+ if request.IsRead() && response.Error != "" {
+ return false
+ }
+ // Defragment is not linearizable
+ if request.Type == model.Defragment {
+ return false
+ }
+ return true
+}
+
+func isSerializable(request model.EtcdRequest, response model.MaybeEtcdResponse) bool {
+ // Cannot test response for request without side effect.
+ if request.IsRead() && response.Error != "" {
+ return false
+ }
+ // Test range requests about stale revision
+ if request.Type == model.Range && request.Range.Revision != 0 {
+ return true
+ }
+ return false
+}
- err = validatePersistedRequestMatchClientRequests(reports, persistedRequests)
+func checkValidationAssumptions(reports []report.ClientReport) error {
+ err := validateEmptyDatabaseAtStart(reports)
if err != nil {
return err
}
+
err = validateNonConcurrentClientRequests(reports)
if err != nil {
return err
@@ -76,84 +125,19 @@ func checkValidationAssumptions(reports []report.ClientReport, persistedRequests
}
func validateEmptyDatabaseAtStart(reports []report.ClientReport) error {
- for _, r := range reports {
- for _, op := range r.KeyValue {
- request := op.Input.(model.EtcdRequest)
- response := op.Output.(model.MaybeEtcdResponse)
- if response.Revision == 2 && !request.IsRead() {
- return nil
- }
- }
- }
- return fmt.Errorf("non empty database at start or first write didn't succeed, required by model implementation")
-}
-
-func validatePersistedRequestMatchClientRequests(reports []report.ClientReport, persistedRequests []model.EtcdRequest) error {
- persistedRequestSet := map[string]model.EtcdRequest{}
- for _, request := range persistedRequests {
- data, err := json.Marshal(request)
- if err != nil {
- return err
- }
- persistedRequestSet[string(data)] = request
+ if len(reports) == 0 {
+ return nil
}
- clientRequests := map[string]porcupine.Operation{}
- for _, r := range reports {
- for _, op := range r.KeyValue {
- request := op.Input.(model.EtcdRequest)
- data, err := json.Marshal(request)
- if err != nil {
- return err
- }
- clientRequests[string(data)] = op
- }
- }
-
- for requestDump, request := range persistedRequestSet {
- _, found := clientRequests[requestDump]
- // We cannot validate if persisted leaseGrant was sent by client as failed leaseGrant will not return LeaseID to clients.
- if request.Type == model.LeaseGrant {
- continue
- }
-
- if !found {
- return fmt.Errorf("request %+v was not sent by client, required to validate", requestDump)
- }
- }
-
- var firstOp, lastOp porcupine.Operation
for _, r := range reports {
for _, op := range r.KeyValue {
request := op.Input.(model.EtcdRequest)
response := op.Output.(model.MaybeEtcdResponse)
- if response.Error != "" || request.IsRead() {
- continue
- }
- if firstOp.Call == 0 || op.Call < firstOp.Call {
- firstOp = op
- }
- if lastOp.Call == 0 || op.Call > lastOp.Call {
- lastOp = op
+ if response.Revision == 1 && request.IsRead() {
+ return nil
}
}
}
- firstOpData, err := json.Marshal(firstOp.Input.(model.EtcdRequest))
- if err != nil {
- return err
- }
- _, found := persistedRequestSet[string(firstOpData)]
- if !found {
- return fmt.Errorf("first succesful client write %s was not persisted, required to validate", firstOpData)
- }
- lastOpData, err := json.Marshal(lastOp.Input.(model.EtcdRequest))
- if err != nil {
- return err
- }
- _, found = persistedRequestSet[string(lastOpData)]
- if !found {
- return fmt.Errorf("last succesful client write %s was not persisted, required to validate", lastOpData)
- }
- return nil
+ return ErrNotEmptyDatabase
}
func validateNonConcurrentClientRequests(reports []report.ClientReport) error {
diff --git a/tests/robustness/validate/validate_test.go b/tests/robustness/validate/validate_test.go
index e847af672a90..2842eb997363 100644
--- a/tests/robustness/validate/validate_test.go
+++ b/tests/robustness/validate/validate_test.go
@@ -21,6 +21,7 @@ import (
"testing"
"time"
+ "github.com/anishathalye/porcupine"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
@@ -34,7 +35,7 @@ func TestDataReports(t *testing.T) {
files, err := os.ReadDir(testdataPath)
require.NoError(t, err)
for _, file := range files {
- if file.Name() == ".gitignore" {
+ if !file.IsDir() {
continue
}
t.Run(file.Name(), func(t *testing.T) {
@@ -44,15 +45,168 @@ func TestDataReports(t *testing.T) {
require.NoError(t, err)
persistedRequests, err := report.LoadClusterPersistedRequests(lg, path)
- require.NoError(t, err)
- visualize := ValidateAndReturnVisualize(t, zaptest.NewLogger(t), Config{}, reports, persistedRequests, 5*time.Minute)
+ if err != nil {
+ t.Error(err)
+ }
+ result := ValidateAndReturnVisualize(zaptest.NewLogger(t), Config{}, reports, persistedRequests, 5*time.Minute)
+ err = result.Error()
+ if err != nil {
+ t.Error(err)
+ }
+
+ err = result.Linearization.Visualize(lg, filepath.Join(path, "history.html"))
+ if err != nil {
+ t.Error(err)
+ }
+ })
+ }
+}
- err = visualize(filepath.Join(path, "history.html"))
+func TestValidateAndReturnVisualize(t *testing.T) {
+ tcs := []struct {
+ name string
+ reports []report.ClientReport
+ persistedRequests []model.EtcdRequest
+ config Config
+ expectError string
+ }{
+ {
+ name: "Success with no persisted requests",
+ reports: []report.ClientReport{
+ {
+ KeyValue: []porcupine.Operation{
+ {
+ ClientId: 0,
+ Input: getRequest("key"),
+ Call: 100,
+ Output: getResponse(1),
+ Return: 200,
+ },
+ },
+ },
+ },
+ expectError: "",
+ },
+ {
+ name: "Failure with not empty database",
+ reports: []report.ClientReport{
+ {
+ KeyValue: []porcupine.Operation{
+ {
+ ClientId: 0,
+ Input: getRequest("key"),
+ Call: 100,
+ // Empty database should have revision 1
+ Output: getResponse(2),
+ Return: 200,
+ },
+ },
+ },
+ },
+ expectError: "non empty database at start, required by model used for linearizability validation",
+ },
+ {
+ name: "Success",
+ reports: []report.ClientReport{
+ {
+ KeyValue: []porcupine.Operation{
+ {
+ ClientId: 0,
+ Input: getRequest("key"),
+ Call: 100,
+ Output: getResponse(1),
+ Return: 200,
+ },
+ {
+ ClientId: 0,
+ Input: putRequest("key", "value"),
+ Call: 300,
+ Output: txnResponse(2, model.EtcdOperationResult{}),
+ Return: 400,
+ },
+ },
+ Watch: []model.WatchOperation{
+ {
+ Request: model.WatchRequest{Key: "key"},
+ Responses: []model.WatchResponse{
+ {Events: []model.WatchEvent{watchEvent(2, true, model.PutOperation, "key", "value")}},
+ },
+ },
+ },
+ },
+ },
+ persistedRequests: []model.EtcdRequest{putRequest("key", "value")},
+ expectError: "",
+ },
+ {
+ name: "Failure of watch",
+ reports: []report.ClientReport{
+ {
+ KeyValue: []porcupine.Operation{
+ {
+ ClientId: 0,
+ Input: getRequest("key"),
+ Call: 100,
+ Output: getResponse(1),
+ Return: 200,
+ },
+ {
+ ClientId: 0,
+ Input: putRequest("key", "value"),
+ Call: 300,
+ Output: txnResponse(2, model.EtcdOperationResult{}),
+ Return: 400,
+ },
+ },
+ Watch: []model.WatchOperation{
+ {
+ Request: model.WatchRequest{Key: "key"},
+ Responses: []model.WatchResponse{
+ {Events: []model.WatchEvent{watchEvent(2, true, model.PutOperation, "key", "value2")}},
+ },
+ },
+ },
+ },
+ },
+ persistedRequests: []model.EtcdRequest{putRequest("key", "value")},
+ expectError: "watch: Failure: broke Reliable",
+ },
+ }
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ lg := zaptest.NewLogger(t)
+ result := ValidateAndReturnVisualize(lg, Config{}, tc.reports, tc.persistedRequests, 5*time.Second)
+
+ if tc.expectError != "" {
+ require.ErrorContains(t, result.Error(), tc.expectError)
+ } else {
+ require.NoError(t, result.Error())
+ }
+ err := result.Linearization.Visualize(lg, filepath.Join(t.TempDir(), "history.html"))
require.NoError(t, err)
})
}
}
+func TestLinearizationVisualizeSkipsDeadlineExceeded(t *testing.T) {
+ lg := zaptest.NewLogger(t)
+ path := filepath.Join(t.TempDir(), "history.html")
+ result := LinearizationResult{
+ Result: Result{
+ Status: DeadlineExceeded,
+ Message: "deadline exceeded",
+ },
+ }
+
+ require.NoError(t, result.Visualize(lg, path))
+ _, err := os.Stat(path)
+ require.Truef(t, os.IsNotExist(err), "deadline exceeded should not produce visualization")
+}
+
+func watchEvent(rev int64, isCreate bool, eventType model.OperationType, key, value string) model.WatchEvent {
+ return model.WatchEvent{PersistedEvent: model.PersistedEvent{Revision: rev, IsCreate: isCreate, Event: model.Event{Type: eventType, Key: key, Value: model.ToValueOrHash(value)}}}
+}
+
func TestValidateWatch(t *testing.T) {
tcs := []struct {
name string
@@ -87,6 +241,39 @@ func TestValidateWatch(t *testing.T) {
putRequest("b", "2"),
},
},
+ {
+ name: "Reliable - Put with non-existent lease doesn't generate watch event - pass",
+ reports: []report.ClientReport{
+ {
+ Watch: []model.WatchOperation{
+ {
+ Request: model.WatchRequest{
+ Key: "a",
+ },
+ Responses: []model.WatchResponse{},
+ },
+ },
+ },
+ },
+ persistedRequests: []model.EtcdRequest{
+ {
+ Type: model.Txn,
+ Txn: &model.TxnRequest{
+ OperationsOnSuccess: []model.EtcdOperation{
+ {
+ Type: model.PutOperation,
+ Put: model.PutOptions{
+ Key: "a",
+ Value: model.ToValueOrHash("1"),
+ LeaseID: 1,
+ },
+ },
+ },
+ },
+ },
+ },
+ expectError: "",
+ },
{
name: "Ordered, Unique - unique ordered events in separate response - pass",
reports: []report.ClientReport{
@@ -1412,7 +1599,7 @@ func TestValidateWatch(t *testing.T) {
},
},
{
- name: "Resumable - missing first matching event - pass",
+ name: "Resumable - missing first matching event - fail",
reports: []report.ClientReport{
{
Watch: []model.WatchOperation{
@@ -1440,7 +1627,7 @@ func TestValidateWatch(t *testing.T) {
expectError: errBrokeResumable.Error(),
},
{
- name: "Resumable - missing first matching event with prefix - pass",
+ name: "Resumable - missing first matching event with prefix - fail",
reports: []report.ClientReport{
{
Watch: []model.WatchOperation{
@@ -1468,7 +1655,7 @@ func TestValidateWatch(t *testing.T) {
expectError: errBrokeResumable.Error(),
},
{
- name: "Resumable - missing first matching event with prefix - pass",
+ name: "Resumable - missing first matching event with prefix - fail",
reports: []report.ClientReport{
{
Watch: []model.WatchOperation{
@@ -1558,7 +1745,7 @@ func TestValidateWatch(t *testing.T) {
expectError: errBrokeIsCreate.Error(),
},
{
- name: "IsCreate - put after delete marked as not created - pass",
+ name: "IsCreate - put after delete marked as not created - fail",
reports: []report.ClientReport{
{
Watch: []model.WatchOperation{
@@ -1839,13 +2026,9 @@ func TestValidateWatch(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
replay := model.NewReplay(tc.persistedRequests)
- err := validateWatch(zaptest.NewLogger(t), tc.config, tc.reports, replay)
- var errStr string
- if err != nil {
- errStr = err.Error()
- }
- if errStr != tc.expectError {
- t.Errorf("validateWatch(...), got: %q, want: %q", err, tc.expectError)
+ result := validateWatch(zaptest.NewLogger(t), tc.config, tc.reports, replay)
+ if result.Message != tc.expectError {
+ t.Errorf("validateWatch(...), got: %q, want: %q", result.Message, tc.expectError)
}
})
}
@@ -1915,6 +2098,21 @@ func deletePersistedEvent(key string, rev int64) model.PersistedEvent {
}
}
+func getRequest(key string) model.EtcdRequest {
+ return model.EtcdRequest{
+ Type: model.Range,
+ Range: &model.RangeRequest{
+ RangeOptions: model.RangeOptions{
+ Start: key,
+ },
+ },
+ }
+}
+
+func getResponse(rev int64) model.MaybeEtcdResponse {
+ return model.MaybeEtcdResponse{EtcdResponse: model.EtcdResponse{Revision: rev, Range: &model.RangeResponse{KVs: []model.KeyValue{}}}}
+}
+
func putRequest(key, value string) model.EtcdRequest {
return model.EtcdRequest{
Type: model.Txn,
diff --git a/tests/robustness/validate/watch.go b/tests/robustness/validate/watch.go
index 506cbeca431f..59d7a8f51f2d 100644
--- a/tests/robustness/validate/watch.go
+++ b/tests/robustness/validate/watch.go
@@ -16,9 +16,11 @@ package validate
import (
"errors"
+ "fmt"
+ "reflect"
+ "time"
"github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
"go.uber.org/zap"
"go.etcd.io/etcd/tests/v3/robustness/model"
@@ -26,7 +28,7 @@ import (
)
var (
- errBrokeBookmarkable = errors.New("broke Bookmarkable - Progress notification events guarantee that all events up to a revision have been already delivered")
+ errBrokeBookmarkable = errors.New("broke Bookmarkable - Progress notification events guarantee that all events up to a revision have already been delivered")
errBrokeOrdered = errors.New("broke Ordered - events are ordered by revision; an event will never appear on a watch if it precedes an event in time that has already been posted")
errBrokeUnique = errors.New("broke Unique - an event will never appear on a watch twice")
errBrokeAtomic = errors.New("broke Atomic - a list of events is guaranteed to encompass complete revisions; updates in the same revision over multiple keys will not be split over several lists of events")
@@ -37,8 +39,19 @@ var (
errBrokeFilter = errors.New("event not matching watch filter")
)
-func validateWatch(lg *zap.Logger, cfg Config, reports []report.ClientReport, replay *model.EtcdReplay) error {
+func validateWatch(lg *zap.Logger, cfg Config, reports []report.ClientReport, replay *model.EtcdReplay) Result {
lg.Info("Validating watch")
+ start := time.Now()
+ err := validateWatchError(lg, cfg, reports, replay)
+ if err != nil {
+ lg.Error("Watch validation failed", zap.Duration("duration", time.Since(start)), zap.Error(err))
+ } else {
+ lg.Info("Watch validation success", zap.Duration("duration", time.Since(start)))
+ }
+ return ResultFromError(err)
+}
+
+func validateWatchError(lg *zap.Logger, cfg Config, reports []report.ClientReport, replay *model.EtcdReplay) error {
// Validate etcd watch properties defined in https://etcd.io/docs/v3.6/learning/api_guarantees/#watch-apis
for _, r := range reports {
err := validateFilter(lg, r)
@@ -65,15 +78,15 @@ func validateWatch(lg *zap.Logger, cfg Config, reports []report.ClientReport, re
if err != nil {
return err
}
- err = validateReliable(lg, replay, r)
+ err = validateIsCreate(lg, replay, r)
if err != nil {
return err
}
- err = validatePrevKV(lg, replay, r)
+ err = validateReliable(lg, replay, r)
if err != nil {
return err
}
- err = validateIsCreate(lg, replay, r)
+ err = validatePrevKV(lg, replay, r)
if err != nil {
return err
}
@@ -205,8 +218,10 @@ func validateReliable(lg *zap.Logger, replay *model.EtcdReplay, report report.Cl
gotEvents = append(gotEvents, event.PersistedEvent)
}
}
- if diff := cmp.Diff(wantEvents, gotEvents, cmpopts.IgnoreFields(model.PersistedEvent{}, "IsCreate")); diff != "" {
- lg.Error("Broke watch guarantee", zap.String("guarantee", "reliable"), zap.Int("client", report.ClientID), zap.String("diff", diff))
+ if !reflect.DeepEqual(wantEvents, gotEvents) {
+ lg.Error("Broke watch guarantee", zap.String("guarantee", "reliable"), zap.Int("client", report.ClientID))
+ // Directly print to console to avoid escaping newline.
+ fmt.Print(cmp.Diff(wantEvents, gotEvents))
err = errBrokeReliable
}
}
@@ -245,7 +260,7 @@ func validatePrevKV(lg *zap.Logger, replay *model.EtcdReplay, report report.Clie
}
for _, resp := range op.Responses {
for _, event := range resp.Events {
- // Get state state just before the current event.
+ // Get state just before the current event.
state, err2 := replay.StateForRevision(event.Revision - 1)
if err2 != nil {
panic(err2)
@@ -263,9 +278,15 @@ func validatePrevKV(lg *zap.Logger, replay *model.EtcdReplay, report report.Clie
// We allow PrevValue to be nil since in the face of compaction, etcd does not
// guarantee its presence.
- if event.PrevValue != nil && *event.PrevValue != state.KeyValues[event.Key] {
- lg.Error("Incorrect event prevValue field", zap.Int("client", report.ClientID), zap.Any("event", event), zap.Any("previousValue", state.KeyValues[event.Key]))
- err = errBrokePrevKV
+ if event.PrevValue != nil {
+ val, ok := state.GetValue(event.Key)
+ if !ok {
+ lg.Error("Incorrect event prevValue field", zap.Int("client", report.ClientID), zap.Any("event", event), zap.Any("previousValue", nil))
+ err = errBrokePrevKV
+ } else if *event.PrevValue != *val {
+ lg.Error("Incorrect event prevValue field", zap.Int("client", report.ClientID), zap.Any("event", event), zap.Any("previousValue", *val))
+ err = errBrokePrevKV
+ }
}
}
}
@@ -277,14 +298,14 @@ func validateIsCreate(lg *zap.Logger, replay *model.EtcdReplay, report report.Cl
for _, op := range report.Watch {
for _, resp := range op.Responses {
for _, event := range resp.Events {
- // Get state state just before the current event.
+ // Get state just before the current event.
state, err2 := replay.StateForRevision(event.Revision - 1)
if err2 != nil {
panic(err2)
}
// A create event will not have an entry in our history and a non-create
// event *should* have an entry in our history.
- if _, prevKeyExists := state.KeyValues[event.Key]; event.IsCreate == prevKeyExists {
+ if _, prevKeyExists := state.GetValue(event.Key); event.IsCreate == prevKeyExists {
lg.Error("Incorrect event IsCreate field", zap.Int("client", report.ClientID), zap.Any("event", event))
err = errBrokeIsCreate
}
diff --git a/tools/.golangci.yaml b/tools/.golangci.yaml
index b4251f1556b3..02090fdd447a 100644
--- a/tools/.golangci.yaml
+++ b/tools/.golangci.yaml
@@ -3,19 +3,44 @@ version: "2"
linters:
default: none
enable: # please keep this alphabetized
- # Disabled after Go 1.24 / golangci-lint v2 update:
- # - errorlint # Disabled as we want to preserve errors from the stable release branch.
- # - testifylint # Disabled as it needs changes in test files to properly
+ - errorlint
+ - goheader
+ - govet
- ineffassign
+ - misspell
- nakedret
- - revive # Disabled as revive from golangci-lint v2 is more strict, it would need more changes in the stable release branch.
+ - revive
- staticcheck
+ - testifylint
+ - thelper
- unconvert # Remove unnecessary type conversions
- unparam
- unused
- usestdlibvars
+ - usetesting
- whitespace
settings:
+ goheader:
+ values:
+ regexp:
+ ORIGINAL_YEAR: 20[1-2][0-9]
+ template: |-
+ Copyright {{ORIGINAL_YEAR}} The etcd Authors
+
+ 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.
+ govet:
+ enable:
+ - shadow
nakedret:
# Align with https://github.com/alexkohler/nakedret/blob/v1.0.2/cmd/nakedret/main.go#L10
max-func-lines: 5
@@ -43,17 +68,19 @@ linters:
arguments:
- preserveScope
- name: time-naming
+ - name: unnecessary-stmt
- name: use-any
- name: var-declaration
- # Disabled after Go 1.24 / golangci-lint v2 update
- name: var-naming
- disabled: true
arguments:
# The following is the configuration for var-naming rule, the first element is the allow list and the second element is the deny list.
- [] # AllowList: leave it empty to use the default (empty, too). This means that we're not relaxing the rule in any way, i.e. elementId will raise a violation, it should be elementID, refer to the next line to see the list of denied initialisms.
# DenyList: Add GRPC and WAL to strict the rule not allowing instances like Wal or Grpc. The default values are located at commonInitialisms, refer to: https://github.com/mgechev/revive/blob/v1.3.7/lint/utils.go#L93-L133.
- - GRPC
- WAL
+ # Disable checks on package names that collide with Go standard library packages. See example error:
+ # var-naming: avoid package names that conflict with Go standard library package names (revive)
+ - - skip-package-name-collision-with-go-std: true
- name: exported
disabled: true
- name: unexported-return
@@ -61,7 +88,6 @@ linters:
staticcheck:
checks:
- all
- - -SA1019 # TODO(fix) Using a deprecated function, variable, constant or field
- -SA2002 # TODO(fix) Called testing.T.FailNow or SkipNow in a goroutine, which isn’t allowed
- -QF1001 # TODO(fix) Apply De Morgan’s law
- -QF1002 # TODO(fix) Convert untagged switch to tagged switch
@@ -87,6 +113,21 @@ linters:
# to always require f-functions for stretchr/testify, but not for golang standard lib.
# Also refer to https://github.com/etcd-io/etcd/pull/18741#issuecomment-2422395914
require-f-funcs: true
+ thelper:
+ test:
+ first: false
+ begin: false
+ fuzz:
+ first: false
+ begin: false
+ benchmark:
+ first: false
+ begin: false
+ tb:
+ first: false
+ begin: false
+ usetesting:
+ os-mkdir-temp: false
exclusions:
generated: lax
presets:
@@ -113,9 +154,15 @@ issues:
max-same-issues: 0
formatters:
enable:
+ - gci
- gofmt
- goimports
settings: # please keep this alphabetized
+ gci:
+ sections:
+ - standard
+ - default
+ - prefix(go.etcd.io)
goimports:
local-prefixes:
- go.etcd.io # Put imports beginning with prefix after 3rd-party packages.
diff --git a/tools/.markdownlint.jsonc b/tools/.markdownlint.jsonc
new file mode 100644
index 000000000000..d6b36639cfc1
--- /dev/null
+++ b/tools/.markdownlint.jsonc
@@ -0,0 +1,282 @@
+// Example markdownlint configuration with all properties set to their default value
+{
+
+ // Default state for all rules
+ "default": true,
+
+ // Path to configuration file to extend
+ "extends": null,
+
+ // MD001/heading-increment : Heading levels should only increment by one level at a time : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md001.md
+ "MD001": true,
+
+ // MD003/heading-style : Heading style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md003.md
+ "MD003": {
+ // Heading style
+ "style": "consistent"
+ },
+
+ // MD004/ul-style : Unordered list style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md004.md
+ "MD004": {
+ // List style
+ "style": "consistent"
+ },
+
+ // MD005/list-indent : Inconsistent indentation for list items at the same level : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md005.md
+ "MD005": true,
+
+ // MD007/ul-indent : Unordered list indentation : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md007.md
+ "MD007": {
+ // Spaces for indent
+ "indent": 2,
+ // Whether to indent the first level of the list
+ "start_indented": false,
+ // Spaces for first level indent (when start_indented is set)
+ "start_indent": 2
+ },
+
+ // MD009/no-trailing-spaces : Trailing spaces : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md009.md
+ "MD009": {
+ // Spaces for line break
+ "br_spaces": 2,
+ // Allow spaces for empty lines in list items
+ "list_item_empty_lines": false,
+ // Include unnecessary breaks
+ "strict": false
+ },
+
+ // MD010/no-hard-tabs : Hard tabs : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md010.md
+ "MD010": {
+ // Include code blocks
+ "code_blocks": true,
+ // Fenced code languages to ignore
+ "ignore_code_languages": [],
+ // Number of spaces for each hard tab
+ "spaces_per_tab": 1
+ },
+
+ // MD011/no-reversed-links : Reversed link syntax : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md011.md
+ "MD011": true,
+
+ // MD012/no-multiple-blanks : Multiple consecutive blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md012.md
+ "MD012": {
+ // Consecutive blank lines
+ "maximum": 1
+ },
+
+ "MD013": false,
+
+ // MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md014.md
+ "MD014": true,
+
+ // MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md018.md
+ "MD018": true,
+
+ // MD019/no-multiple-space-atx : Multiple spaces after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md019.md
+ "MD019": true,
+
+ // MD020/no-missing-space-closed-atx : No space inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md020.md
+ "MD020": true,
+
+ // MD021/no-multiple-space-closed-atx : Multiple spaces inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md021.md
+ "MD021": true,
+
+ // MD022/blanks-around-headings : Headings should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md022.md
+ "MD022": {
+ // Blank lines above heading
+ "lines_above": 1,
+ // Blank lines below heading
+ "lines_below": 1
+ },
+
+ // MD023/heading-start-left : Headings must start at the beginning of the line : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md023.md
+ "MD023": true,
+
+ // MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md024.md
+ "MD024": {
+ // Only check sibling headings
+ "siblings_only": false
+ },
+
+ // MD025/single-title/single-h1 : Multiple top-level headings in the same document : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md025.md
+ "MD025": {
+ // Heading level
+ "level": 1,
+ // RegExp for matching title in front matter
+ "front_matter_title": "^\\s*title\\s*[:=]"
+ },
+
+ // MD026/no-trailing-punctuation : Trailing punctuation in heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md026.md
+ "MD026": {
+ // Punctuation characters
+ "punctuation": ".,;:!。,;:!"
+ },
+
+ // MD027/no-multiple-space-blockquote : Multiple spaces after blockquote symbol : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md027.md
+ "MD027": true,
+
+ // MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md028.md
+ "MD028": true,
+
+ // MD029/ol-prefix : Ordered list item prefix : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md029.md
+ "MD029": {
+ // List style
+ "style": "one_or_ordered"
+ },
+
+ // MD030/list-marker-space : Spaces after list markers : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md030.md
+ "MD030": {
+ // Spaces for single-line unordered list items
+ "ul_single": 1,
+ // Spaces for single-line ordered list items
+ "ol_single": 1,
+ // Spaces for multi-line unordered list items
+ "ul_multi": 1,
+ // Spaces for multi-line ordered list items
+ "ol_multi": 1
+ },
+
+ // MD031/blanks-around-fences : Fenced code blocks should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md031.md
+ "MD031": {
+ // Include list items
+ "list_items": true
+ },
+
+ // MD032/blanks-around-lists : Lists should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
+ "MD032": true,
+
+ // MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md033.md
+ "MD033": {
+ // Allowed elements
+ "allowed_elements": []
+ },
+
+ // MD034/no-bare-urls : Bare URL used : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md034.md
+ "MD034": true,
+
+ // MD035/hr-style : Horizontal rule style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md035.md
+ "MD035": {
+ // Horizontal rule style
+ "style": "consistent"
+ },
+
+ // MD036/no-emphasis-as-heading : Emphasis used instead of a heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md036.md
+ "MD036": {
+ // Punctuation characters
+ "punctuation": ".,;:!?。,;:!?"
+ },
+
+ // MD037/no-space-in-emphasis : Spaces inside emphasis markers : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md037.md
+ "MD037": true,
+
+ // MD038/no-space-in-code : Spaces inside code span elements : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md038.md
+ "MD038": true,
+
+ // MD039/no-space-in-links : Spaces inside link text : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md039.md
+ "MD039": true,
+
+ // MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md040.md
+ "MD040": {
+ // List of languages
+ "allowed_languages": [],
+ // Require language only
+ "language_only": false
+ },
+
+ // MD041/first-line-heading/first-line-h1 : First line in a file should be a top-level heading : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md041.md
+ "MD041": false,
+
+ // MD042/no-empty-links : No empty links : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md042.md
+ "MD042": true,
+
+ // MD043/required-headings : Required heading structure : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md043.md
+ "MD043": false,
+
+ // MD044/proper-names : Proper names should have the correct capitalization : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md044.md
+ "MD044": {
+ // List of proper names
+ "names": [],
+ // Include code blocks
+ "code_blocks": true,
+ // Include HTML elements
+ "html_elements": true
+ },
+
+ // MD045/no-alt-text : Images should have alternate text (alt text) : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md045.md
+ "MD045": true,
+
+ // MD046/code-block-style : Code block style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md046.md
+ "MD046": {
+ // Block style
+ "style": "consistent"
+ },
+
+ // MD047/single-trailing-newline : Files should end with a single newline character : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md047.md
+ "MD047": true,
+
+ // MD048/code-fence-style : Code fence style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md048.md
+ "MD048": {
+ // Code fence style
+ "style": "consistent"
+ },
+
+ // MD049/emphasis-style : Emphasis style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md049.md
+ "MD049": {
+ // Emphasis style
+ "style": "consistent"
+ },
+
+ // MD050/strong-style : Strong style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md050.md
+ "MD050": {
+ // Strong style
+ "style": "consistent"
+ },
+
+ // MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md051.md
+ "MD051": {
+ // Ignore case of fragments
+ "ignore_case": false
+ },
+
+ // MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md052.md
+ "MD052": {
+ // Include shortcut syntax
+ "shortcut_syntax": false
+ },
+
+ // MD053/link-image-reference-definitions : Link and image reference definitions should be needed : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md053.md
+ "MD053": {
+ // Ignored definitions
+ "ignored_definitions": [
+ "//"
+ ]
+ },
+
+ // MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md054.md
+ "MD054": {
+ // Allow autolinks
+ "autolink": true,
+ // Allow inline links and images
+ "inline": true,
+ // Allow full reference links and images
+ "full": true,
+ // Allow collapsed reference links and images
+ "collapsed": true,
+ // Allow shortcut reference links and images
+ "shortcut": true,
+ // Allow URLs as inline links
+ "url_inline": true
+ },
+
+ // MD055/table-pipe-style : Table pipe style : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md055.md
+ "MD055": {
+ // Table pipe style
+ "style": "consistent"
+ },
+
+ // MD056/table-column-count : Table column count : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md056.md
+ "MD056": true,
+
+ // MD058/blanks-around-tables : Tables should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md058.md
+ "MD058": true
+ }
diff --git a/tools/benchmark/cmd/lease.go b/tools/benchmark/cmd/lease.go
index 5ed3dbe4b3e0..6eb26bff1e74 100644
--- a/tools/benchmark/cmd/lease.go
+++ b/tools/benchmark/cmd/lease.go
@@ -40,14 +40,14 @@ func init() {
leaseKeepaliveCmd.Flags().IntVar(&leaseKeepaliveTotal, "total", 10000, "Total number of lease keepalive requests")
}
-func leaseKeepaliveFunc(_ *cobra.Command, _ []string) {
+func leaseKeepaliveFunc(cmd *cobra.Command, _ []string) {
requests := make(chan struct{})
clients := mustCreateClients(totalClients, totalConns)
bar = pb.New(leaseKeepaliveTotal)
bar.Start()
- r := newReport()
+ r := newReport(cmd.Name())
for i := range clients {
wg.Add(1)
go func(c v3.Lease) {
@@ -65,14 +65,12 @@ func leaseKeepaliveFunc(_ *cobra.Command, _ []string) {
}(clients[i])
}
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
for i := 0; i < leaseKeepaliveTotal; i++ {
requests <- struct{}{}
}
close(requests)
- }()
+ })
rc := r.Run()
wg.Wait()
diff --git a/tools/benchmark/cmd/mvcc-put.go b/tools/benchmark/cmd/mvcc-put.go
index 25cdb61d5417..b6a6379c23c1 100644
--- a/tools/benchmark/cmd/mvcc-put.go
+++ b/tools/benchmark/cmd/mvcc-put.go
@@ -21,11 +21,11 @@ import (
"runtime/pprof"
"time"
+ "github.com/spf13/cobra"
+
"go.etcd.io/etcd/pkg/v3/report"
"go.etcd.io/etcd/pkg/v3/traceutil"
"go.etcd.io/etcd/server/v3/lease"
-
- "github.com/spf13/cobra"
)
// mvccPutCmd represents a storage put performance benchmarking tool
@@ -69,7 +69,7 @@ func createBytesSlice(bytesN, sliceN int) [][]byte {
return rs
}
-func mvccPutFunc(_ *cobra.Command, _ []string) {
+func mvccPutFunc(cmd *cobra.Command, _ []string) {
if cpuProfPath != "" {
f, err := os.Create(cpuProfPath)
if err != nil {
@@ -105,7 +105,7 @@ func mvccPutFunc(_ *cobra.Command, _ []string) {
vals := createBytesSlice(valueSize, mvccTotalRequests*nrTxnOps)
weight := float64(nrTxnOps)
- r := newWeightedReport()
+ r := newWeightedReport(cmd.Name())
rrc := r.Results()
rc := r.Run()
diff --git a/tools/benchmark/cmd/mvcc.go b/tools/benchmark/cmd/mvcc.go
index baf2bf1a1bb6..67e3f7400705 100644
--- a/tools/benchmark/cmd/mvcc.go
+++ b/tools/benchmark/cmd/mvcc.go
@@ -18,13 +18,12 @@ import (
"os"
"time"
+ "github.com/spf13/cobra"
"go.uber.org/zap"
"go.etcd.io/etcd/server/v3/lease"
"go.etcd.io/etcd/server/v3/storage/backend"
"go.etcd.io/etcd/server/v3/storage/mvcc"
-
- "github.com/spf13/cobra"
)
var (
diff --git a/tools/benchmark/cmd/put.go b/tools/benchmark/cmd/put.go
index 62b9a3dc83cc..7f66046f2109 100644
--- a/tools/benchmark/cmd/put.go
+++ b/tools/benchmark/cmd/put.go
@@ -88,7 +88,7 @@ func putFunc(cmd *cobra.Command, _ []string) {
bar = pb.New(putTotal)
bar.Start()
- r := newReport()
+ r := newReport(cmd.Name())
for i := range clients {
wg.Add(1)
go func(c *v3.Client) {
diff --git a/tools/benchmark/cmd/range.go b/tools/benchmark/cmd/range.go
index 111d69bd0558..4d5b520ca703 100644
--- a/tools/benchmark/cmd/range.go
+++ b/tools/benchmark/cmd/range.go
@@ -22,13 +22,19 @@ import (
"time"
"github.com/cheggaaa/pb/v3"
+ "github.com/golang/protobuf/proto" //nolint:staticcheck
"github.com/spf13/cobra"
"golang.org/x/time/rate"
+ etcdserverpb "go.etcd.io/etcd/api/v3/etcdserverpb"
v3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/pkg/v3/report"
)
+// k8sWatchCachePageSize matches the default page size used by the Kubernetes
+// watch cache when paginating List requests against etcd.
+const k8sWatchCachePageSize = 10000
+
// rangeCmd represents the range command
var rangeCmd = &cobra.Command{
Use: "range key [end-range]",
@@ -43,6 +49,10 @@ var (
rangeConsistency string
rangeLimit int64
rangeCountOnly bool
+ rangeKeysOnly bool
+ rangeStream bool
+ rangePaginate bool
+ rangePrefix bool
)
func init() {
@@ -52,20 +62,37 @@ func init() {
rangeCmd.Flags().StringVar(&rangeConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
rangeCmd.Flags().Int64Var(&rangeLimit, "limit", 0, "Maximum number of results to return from range request (0 is no limit)")
rangeCmd.Flags().BoolVar(&rangeCountOnly, "count-only", false, "Only returns the count of keys")
+ rangeCmd.Flags().BoolVar(&rangeKeysOnly, "keys-only", false, "Only returns the keys")
+ rangeCmd.Flags().BoolVar(&rangeStream, "stream", false, "Use RangeStream instead of unary Range")
+ rangeCmd.Flags().BoolVar(&rangePaginate, "paginate", false, "Use paginated unary range with 10k-key pages")
+ rangeCmd.Flags().BoolVar(&rangePrefix, "prefix", false, "Range over all keys with the given key as prefix")
}
func rangeFunc(cmd *cobra.Command, args []string) {
- if len(args) == 0 || len(args) > 2 {
+ if len(args) > 2 || (len(args) == 0 && !rangePrefix) {
fmt.Fprintln(os.Stderr, cmd.Usage())
os.Exit(1)
}
- k := args[0]
+ key := ""
end := ""
+ if len(args) >= 1 {
+ key = args[0]
+ }
if len(args) == 2 {
end = args[1]
}
+ if rangePaginate && rangeLimit > 0 {
+ fmt.Fprintln(os.Stderr, "--paginate and --limit are mutually exclusive")
+ os.Exit(1)
+ }
+
+ if rangeCountOnly && rangeKeysOnly {
+ fmt.Fprintln(os.Stderr, "`--keys-only` and `--count-only` cannot be set at the same time")
+ os.Exit(1)
+ }
+
if rangeConsistency == "l" {
fmt.Println("bench with linearizable range")
} else if rangeConsistency == "s" {
@@ -80,22 +107,63 @@ func rangeFunc(cmd *cobra.Command, args []string) {
}
limit := rate.NewLimiter(rate.Limit(rangeRate), 1)
- requests := make(chan v3.Op, totalClients)
+ requests := make(chan struct{}, totalClients)
clients := mustCreateClients(totalClients, totalConns)
bar = pb.New(rangeTotal)
bar.Start()
- r := newReport()
+ var baseOpts []v3.OpOption
+ if rangeLimit > 0 {
+ baseOpts = append(baseOpts, v3.WithLimit(rangeLimit))
+ }
+
+ switch {
+ case rangeCountOnly:
+ baseOpts = append(baseOpts, v3.WithCountOnly())
+ case rangeKeysOnly:
+ baseOpts = append(baseOpts, v3.WithKeysOnly())
+ }
+
+ if rangeConsistency == "s" {
+ baseOpts = append(baseOpts, v3.WithSerializable())
+ }
+ if rangePrefix {
+ if rangePaginate {
+ // Pin the prefix's range end once. Otherwise WithPrefix would
+ // re-derive it from the advancing start key on every page.
+ baseOpts = append(baseOpts, v3.WithRange(v3.GetPrefixRangeEnd(key)))
+ if key == "" {
+ key = "\x00"
+ }
+ } else {
+ baseOpts = append(baseOpts, v3.WithPrefix())
+ }
+ } else if end != "" {
+ baseOpts = append(baseOpts, v3.WithRange(end))
+ }
+
+ r := newReport(cmd.Name())
for i := range clients {
wg.Add(1)
go func(c *v3.Client) {
defer wg.Done()
- for op := range requests {
+ for range requests {
limit.Wait(context.Background())
-
st := time.Now()
- _, err := c.Do(context.Background(), op)
+ var err error
+ switch {
+ case rangeStream:
+ var stream v3.GetStreamChan
+ stream, err = c.GetStream(context.Background(), key, baseOpts...)
+ if err == nil {
+ _, err = v3.GetStreamToGetResponse(stream)
+ }
+ case rangePaginate:
+ err = paginatedRange(c, key, k8sWatchCachePageSize, baseOpts)
+ default:
+ _, err = c.Get(context.Background(), key, baseOpts...)
+ }
r.Results() <- report.Result{Err: err, Start: st, End: time.Now()}
bar.Increment()
}
@@ -104,15 +172,7 @@ func rangeFunc(cmd *cobra.Command, args []string) {
go func() {
for i := 0; i < rangeTotal; i++ {
- opts := []v3.OpOption{v3.WithRange(end), v3.WithLimit(rangeLimit)}
- if rangeCountOnly {
- opts = append(opts, v3.WithCountOnly())
- }
- if rangeConsistency == "s" {
- opts = append(opts, v3.WithSerializable())
- }
- op := v3.OpGet(k, opts...)
- requests <- op
+ requests <- struct{}{}
}
close(requests)
}()
@@ -123,3 +183,27 @@ func rangeFunc(cmd *cobra.Command, args []string) {
bar.Finish()
fmt.Printf("%s", <-rc)
}
+
+func paginatedRange(c *v3.Client, key string, pageSize int64, baseOpts []v3.OpOption) error {
+ merged := &etcdserverpb.RangeResponse{}
+ var rev int64
+ for {
+ opts := append([]v3.OpOption{v3.WithLimit(pageSize)}, baseOpts...)
+ if rev != 0 {
+ opts = append(opts, v3.WithRev(rev))
+ }
+ resp, err := c.Get(context.Background(), key, opts...)
+ if err != nil {
+ return err
+ }
+ if rev == 0 {
+ rev = resp.Header.Revision
+ }
+ proto.Merge(merged, (*etcdserverpb.RangeResponse)(resp))
+ if !resp.More || len(resp.Kvs) == 0 {
+ return nil
+ }
+ last := resp.Kvs[len(resp.Kvs)-1].Key
+ key = string(append(last, '\x00'))
+ }
+}
diff --git a/tools/benchmark/cmd/root.go b/tools/benchmark/cmd/root.go
index c85beb9dbc65..f3768db1006c 100644
--- a/tools/benchmark/cmd/root.go
+++ b/tools/benchmark/cmd/root.go
@@ -53,8 +53,9 @@ var (
dialTimeout time.Duration
- targetLeader bool
autoSyncInterval time.Duration
+
+ generatePerfReport bool
)
func init() {
@@ -72,6 +73,7 @@ func init() {
RootCmd.PersistentFlags().StringVar(&user, "user", "", "provide username[:password] and prompt if password is not supplied.")
RootCmd.PersistentFlags().DurationVar(&dialTimeout, "dial-timeout", 0, "dial timeout for client connections")
- RootCmd.PersistentFlags().BoolVar(&targetLeader, "target-leader", false, "connect only to the leader node")
RootCmd.PersistentFlags().DurationVar(&autoSyncInterval, "auto-sync-interval", time.Duration(0), "AutoSyncInterval is the interval to update endpoints with its latest members")
+
+ RootCmd.PersistentFlags().BoolVar(&generatePerfReport, "report-perfdash", false, "Generate benchmark report in perfdash format")
}
diff --git a/tools/benchmark/cmd/stm.go b/tools/benchmark/cmd/stm.go
index d6dfba0d397e..22dc88657142 100644
--- a/tools/benchmark/cmd/stm.go
+++ b/tools/benchmark/cmd/stm.go
@@ -110,7 +110,7 @@ func stmFunc(cmd *cobra.Command, _ []string) {
bar = pb.New(stmTotal)
bar.Start()
- r := newReport()
+ r := newReport(cmd.Name())
for i := range clients {
wg.Add(1)
go doSTM(clients[i], requests, r.Results())
diff --git a/tools/benchmark/cmd/txn_mixed.go b/tools/benchmark/cmd/txn_mixed.go
index ffc004ecfe20..bbd88d599c09 100644
--- a/tools/benchmark/cmd/txn_mixed.go
+++ b/tools/benchmark/cmd/txn_mixed.go
@@ -95,8 +95,8 @@ func mixedTxnFunc(cmd *cobra.Command, _ []string) {
bar = pb.New(mixedTxnTotal)
bar.Start()
- reportRead := newReport()
- reportWrite := newReport()
+ reportRead := newReport(cmd.Name() + "-read")
+ reportWrite := newReport(cmd.Name() + "-write")
for i := range clients {
wg.Add(1)
go func(c *v3.Client) {
diff --git a/tools/benchmark/cmd/txn_put.go b/tools/benchmark/cmd/txn_put.go
index d69293b838a8..5e2d9d7a4073 100644
--- a/tools/benchmark/cmd/txn_put.go
+++ b/tools/benchmark/cmd/txn_put.go
@@ -55,7 +55,7 @@ func init() {
txnPutCmd.Flags().IntVar(&keySpaceSize, "key-space-size", 1, "Maximum possible keys")
}
-func txnPutFunc(_ *cobra.Command, _ []string) {
+func txnPutFunc(cmd *cobra.Command, _ []string) {
if keySpaceSize <= 0 {
fmt.Fprintf(os.Stderr, "expected positive --key-space-size, got (%v)", keySpaceSize)
os.Exit(1)
@@ -78,7 +78,7 @@ func txnPutFunc(_ *cobra.Command, _ []string) {
bar = pb.New(txnPutTotal)
bar.Start()
- r := newReport()
+ r := newReport(cmd.Name())
for i := range clients {
wg.Add(1)
go func(c *v3.Client) {
diff --git a/tools/benchmark/cmd/util.go b/tools/benchmark/cmd/util.go
index a6b999390671..63fa9802da25 100644
--- a/tools/benchmark/cmd/util.go
+++ b/tools/benchmark/cmd/util.go
@@ -15,7 +15,6 @@
package cmd
import (
- "context"
"crypto/rand"
"fmt"
"os"
@@ -29,50 +28,18 @@ import (
)
var (
- // dialTotal counts the number of mustCreateConn calls so that endpoint
- // connections can be handed out in round-robin order
- dialTotal int
-
- // leaderEps is a cache for holding endpoints of a leader node
- leaderEps []string
-
// cache the username and password for multiple connections
globalUserName string
globalPassword string
)
-func mustFindLeaderEndpoints(c *clientv3.Client) {
- resp, lerr := c.MemberList(context.TODO())
- if lerr != nil {
- fmt.Fprintf(os.Stderr, "failed to get a member list: %s\n", lerr)
- os.Exit(1)
- }
-
- leaderID := uint64(0)
- for _, ep := range c.Endpoints() {
- if sresp, serr := c.Status(context.TODO(), ep); serr == nil {
- leaderID = sresp.Leader
- break
- }
- }
-
- for _, m := range resp.Members {
- if m.ID == leaderID {
- leaderEps = m.ClientURLs
- return
- }
- }
-
- fmt.Fprint(os.Stderr, "failed to find a leader endpoint\n")
- os.Exit(1)
-}
-
func getUsernamePassword(usernameFlag string) (string, string, error) {
if globalUserName != "" && globalPassword != "" {
return globalUserName, globalPassword, nil
}
- colon := strings.Index(usernameFlag, ":")
- if colon == -1 {
+ var ok bool
+ globalUserName, globalPassword, ok = strings.Cut(usernameFlag, ":")
+ if !ok {
// Prompt for the password.
password, err := speakeasy.Ask("Password: ")
if err != nil {
@@ -80,22 +47,14 @@ func getUsernamePassword(usernameFlag string) (string, string, error) {
}
globalUserName = usernameFlag
globalPassword = password
- } else {
- globalUserName = usernameFlag[:colon]
- globalPassword = usernameFlag[colon+1:]
}
return globalUserName, globalPassword, nil
}
func mustCreateConn() *clientv3.Client {
- connEndpoints := leaderEps
- if len(connEndpoints) == 0 {
- connEndpoints = []string{endpoints[dialTotal%len(endpoints)]}
- dialTotal++
- }
cfg := clientv3.Config{
AutoSyncInterval: autoSyncInterval,
- Endpoints: connEndpoints,
+ Endpoints: endpoints,
DialTimeout: dialTimeout,
}
if !tls.Empty() || tls.TrustedCAFile != "" {
@@ -118,12 +77,6 @@ func mustCreateConn() *clientv3.Client {
}
client, err := clientv3.New(cfg)
- if targetLeader && len(leaderEps) == 0 {
- mustFindLeaderEndpoints(client)
- client.Close()
- return mustCreateConn()
- }
-
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
if err != nil {
@@ -157,24 +110,24 @@ func mustRandBytes(n int) []byte {
return rb
}
-func newReport() report.Report {
+func newReport(benchmarkOp string) report.Report {
p := "%4.4f"
if precise {
p = "%g"
}
if sample {
- return report.NewReportSample(p)
+ return report.NewReportSample(p, benchmarkOp, generatePerfReport)
}
- return report.NewReport(p)
+ return report.NewReport(p, benchmarkOp, generatePerfReport)
}
-func newWeightedReport() report.Report {
+func newWeightedReport(benchmarkOp string) report.Report {
p := "%4.4f"
if precise {
p = "%g"
}
if sample {
- return report.NewReportSample(p)
+ return report.NewReportSample(p, benchmarkOp, generatePerfReport)
}
- return report.NewWeightedReport(report.NewReport(p), p)
+ return report.NewWeightedReport(report.NewReport(p, benchmarkOp, generatePerfReport), p, benchmarkOp, generatePerfReport)
}
diff --git a/tools/benchmark/cmd/watch.go b/tools/benchmark/cmd/watch.go
index a52a34360f2b..7b707bf96aae 100644
--- a/tools/benchmark/cmd/watch.go
+++ b/tools/benchmark/cmd/watch.go
@@ -117,7 +117,7 @@ func benchMakeWatches(clients []*clientv3.Client, wk *watchedKeys) {
bar = pb.New(watchStreams * watchWatchesPerStream)
bar.Start()
- r := newReport()
+ r := newReport("watch-make")
rch := r.Results()
wg.Add(len(streams) + 1)
@@ -189,7 +189,7 @@ func benchPutWatches(clients []*clientv3.Client, wk *watchedKeys) {
bar = pb.New(eventsTotal)
bar.Start()
- r := newReport()
+ r := newReport("watch-put")
wg.Add(len(wk.watches))
nrRxed := int32(eventsTotal)
diff --git a/tools/benchmark/cmd/watch_get.go b/tools/benchmark/cmd/watch_get.go
index b9a8bc3363d5..96abad665c75 100644
--- a/tools/benchmark/cmd/watch_get.go
+++ b/tools/benchmark/cmd/watch_get.go
@@ -49,7 +49,7 @@ func init() {
watchGetCmd.Flags().IntVar(&watchEvents, "events", 8, "Number of events per watcher")
}
-func watchGetFunc(_ *cobra.Command, _ []string) {
+func watchGetFunc(cmd *cobra.Command, _ []string) {
clients := mustCreateClients(totalClients, totalConns)
getClient := mustCreateClients(1, 1)
@@ -75,7 +75,7 @@ func watchGetFunc(_ *cobra.Command, _ []string) {
bar.Start()
// report from trying to do serialized gets with concurrent watchers
- r := newReport()
+ r := newReport(cmd.Name())
ctx, cancel := context.WithCancel(context.TODO())
f := func() {
defer close(r.Results())
diff --git a/tools/benchmark/cmd/watch_latency.go b/tools/benchmark/cmd/watch_latency.go
index 2e60860b460d..59fa689b4148 100644
--- a/tools/benchmark/cmd/watch_latency.go
+++ b/tools/benchmark/cmd/watch_latency.go
@@ -60,7 +60,7 @@ func init() {
watchLatencyCmd.Flags().IntVar(&watchLValueSize, "val-size", 32, "Value size of watch response")
}
-func watchLatencyFunc(_ *cobra.Command, _ []string) {
+func watchLatencyFunc(cmd *cobra.Command, _ []string) {
key := string(mustRandBytes(watchLKeySize))
value := string(mustRandBytes(watchLValueSize))
wchs := setupWatchChannels(key)
@@ -75,12 +75,8 @@ func watchLatencyFunc(_ *cobra.Command, _ []string) {
eventTimes := make([][]time.Time, len(wchs))
for i, wch := range wchs {
- wch := wch
- i := i
eventTimes[i] = make([]time.Time, watchLPutTotal)
- wg.Add(1)
- go func() {
- defer wg.Done()
+ wg.Go(func() {
eventCount := 0
for eventCount < watchLPutTotal {
resp := <-wch
@@ -90,12 +86,12 @@ func watchLatencyFunc(_ *cobra.Command, _ []string) {
bar.Increment()
}
}
- }()
+ })
}
- putReport := newReport()
+ putReport := newReport(cmd.Name() + "-put")
putReportResults := putReport.Run()
- watchReport := newReport()
+ watchReport := newReport(cmd.Name() + "-watch")
watchReportResults := watchReport.Run()
for i := 0; i < watchLPutTotal; i++ {
// limit key put as per reqRate
diff --git a/tools/check-grpc-experimental/allowlist.txt b/tools/check-grpc-experimental/allowlist.txt
new file mode 100644
index 000000000000..6c4b395db0d2
--- /dev/null
+++ b/tools/check-grpc-experimental/allowlist.txt
@@ -0,0 +1,21 @@
+# Allowlist for experimental gRPC APIs
+# Format: PackageName.Symbol
+# Remove items from this list as they are migrated or stabilized.
+
+grpc.NewContextWithServerTransportStream
+grpc.ServeHTTP
+grpc.WithResolvers
+resolver.Address
+resolver.BuildOptions
+resolver.Builder
+resolver.ClientConn
+resolver.Endpoint
+resolver.ParseServiceConfig
+resolver.ResolveNowOptions
+resolver.Resolver
+resolver.State
+resolver.Target
+resolver.URL
+resolver.UpdateState
+serviceconfig.Err
+serviceconfig.ParseResult
diff --git a/tools/check-grpc-experimental/doc.go b/tools/check-grpc-experimental/doc.go
new file mode 100644
index 000000000000..b1f4273b393e
--- /dev/null
+++ b/tools/check-grpc-experimental/doc.go
@@ -0,0 +1,16 @@
+// Copyright 2026 The etcd Authors
+//
+// 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.
+
+// check-grpc-experimental checks for experimental gRPC APIs in etcd.
+package main
diff --git a/tools/check-grpc-experimental/main.go b/tools/check-grpc-experimental/main.go
new file mode 100644
index 000000000000..a41c79e6f2f8
--- /dev/null
+++ b/tools/check-grpc-experimental/main.go
@@ -0,0 +1,325 @@
+// Copyright 2026 The etcd Authors
+//
+// 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 main
+
+import (
+ "bufio"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "log"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "sync"
+
+ "golang.org/x/tools/go/packages"
+)
+
+var (
+ debugMode = flag.Bool("debug", false, "enable verbose debug logging")
+ allowListFile = flag.String("allow-list", "", "path to a file containing allowed APIs (one per line)")
+)
+
+// Map to store allowed signatures (e.g., "grpc.WithResolvers" -> true)
+var allowList = make(map[string]bool)
+
+func main() {
+ flag.Parse()
+ patterns := flag.Args()
+ if len(patterns) == 0 {
+ patterns = []string{"./..."}
+ }
+
+ if *allowListFile != "" {
+ if err := loadAllowList(*allowListFile); err != nil {
+ log.Fatalf("Failed to load allow list: %v", err)
+ }
+ }
+
+ // Load source with type info.
+ cfg := &packages.Config{
+ Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports | packages.NeedDeps,
+ Tests: true,
+ }
+
+ if *debugMode {
+ log.Println("Loading packages...")
+ }
+
+ pkgs, err := packages.Load(cfg, patterns...)
+ if err != nil {
+ log.Fatalf("failed to load packages: %v", err)
+ }
+ if n := packages.PrintErrors(pkgs); n > 0 {
+ os.Exit(1)
+ }
+
+ if *debugMode {
+ log.Printf("Loaded %d packages. Scanning for gRPC usage...", len(pkgs))
+ }
+
+ foundExperimental := false
+
+ for _, pkg := range pkgs {
+ for _, file := range pkg.Syntax {
+ ast.Inspect(file, func(n ast.Node) bool {
+ sel, ok := n.(*ast.SelectorExpr)
+ if !ok {
+ return true
+ }
+
+ obj := pkg.TypesInfo.Uses[sel.Sel]
+ if obj == nil || obj.Pkg() == nil {
+ return true
+ }
+
+ // Strict filter for gRPC
+ if !strings.Contains(obj.Pkg().Path(), "google.golang.org/grpc") {
+ return true
+ }
+
+ // Check Allowlist
+ // Construct the signature: PackageName.Symbol (e.g. "grpc.WithResolvers", "resolver.Address")
+ signature := obj.Pkg().Name() + "." + obj.Name()
+ if allowList[signature] {
+ if *debugMode {
+ log.Printf("Ignoring allowed usage: %s", signature)
+ }
+ return true
+ }
+
+ if *debugMode {
+ log.Printf("Checking reference: %s", signature)
+ }
+
+ if isExperimental(pkg.Fset, obj) {
+ pos := pkg.Fset.Position(sel.Pos())
+ fmt.Printf("%s:%d:%d: usage of experimental gRPC API: %s\n",
+ pos.Filename, pos.Line, pos.Column, signature)
+ foundExperimental = true
+ }
+
+ return true
+ })
+ }
+ }
+
+ if foundExperimental {
+ os.Exit(1)
+ }
+}
+
+var (
+ experimentalRegex = regexp.MustCompile(`(?i)(#\s*Experimental|All APIs in this package are experimental|This API is EXPERIMENTAL|is currently experimental)`)
+)
+
+func loadAllowList(fpath string) error {
+ f, err := os.Open(fpath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ allowList[line] = true
+ }
+ return scanner.Err()
+}
+
+func isExperimental(mainFset *token.FileSet, obj types.Object) bool {
+ pos := obj.Pos()
+ if !pos.IsValid() {
+ return false
+ }
+
+ // Get the absolute path to the dependency file
+ position := mainFset.Position(pos)
+ filename := position.Filename
+
+ // Skip if it's not a Go file (e.g. built-in types might have no file)
+ if !strings.HasSuffix(filename, ".go") {
+ return false
+ }
+
+ if *debugMode {
+ // Log checking definition
+ log.Printf(" -> Definition found at: %s:%d", filename, position.Line)
+ }
+
+ return checkFileForExperimental(filename, position.Line)
+}
+
+// Cached file structure
+type cachedFile struct {
+ f *ast.File
+ fset *token.FileSet
+ src []byte
+ hasDocs bool
+}
+
+var (
+ cacheMu sync.RWMutex
+ advancedCache = make(map[string]*cachedFile)
+)
+
+func getParsedFile(filename string) (*cachedFile, error) {
+ cacheMu.RLock()
+ if v, ok := advancedCache[filename]; ok {
+ cacheMu.RUnlock()
+ return v, nil
+ }
+ cacheMu.RUnlock()
+
+ // Parse the file
+ fset := token.NewFileSet()
+ // We read the file content manually to help with debugging if needed
+ src, err := os.ReadFile(filename)
+ if err != nil {
+ if *debugMode {
+ log.Printf("ERROR reading file %s: %v", filename, err)
+ }
+ return nil, err
+ }
+
+ f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.SkipObjectResolution)
+ if err != nil {
+ return nil, err
+ }
+
+ res := &cachedFile{f: f, fset: fset, src: src, hasDocs: f.Doc != nil}
+
+ cacheMu.Lock()
+ advancedCache[filename] = res
+ cacheMu.Unlock()
+
+ return res, nil
+}
+
+func checkFileForExperimental(filename string, targetLine int) bool {
+ cf, err := getParsedFile(filename)
+ if err != nil {
+ return false
+ }
+
+ // check package-level comments
+ if cf.f.Doc != nil {
+ if experimentalRegex.MatchString(cf.f.Doc.Text()) {
+ if *debugMode {
+ log.Printf(" -> [MATCH] Package experimental (doc in file): %s", filename)
+ }
+ return true
+ }
+ }
+
+ // check package-level comment in doc.go
+ // If the current file didn't have the experimental tag, check if a doc.go exists in the same folder
+ dir := filepath.Dir(filename)
+ docPath := filepath.Join(dir, "doc.go")
+ // Only check doc.go if we aren't already looking at it
+ if docPath != filename {
+ if docContent, err := os.ReadFile(docPath); err == nil {
+ if experimentalRegex.Match(docContent) {
+ if *debugMode {
+ log.Printf(" -> [MATCH] Package experimental (found in doc.go): %s", docPath)
+ }
+ return true
+ }
+ }
+ }
+
+ // check specific object comments
+ found := false
+
+ ast.Inspect(cf.f, func(n ast.Node) bool {
+ if found {
+ return false
+ }
+ if n == nil {
+ return true
+ }
+
+ // Helper to check a comment group
+ checkDoc := func(doc *ast.CommentGroup, name string) {
+ if doc != nil && experimentalRegex.MatchString(doc.Text()) {
+ found = true
+ if *debugMode {
+ log.Printf(" -> [MATCH] Object experimental: %s", name)
+ }
+ }
+ }
+
+ switch decl := n.(type) {
+ case *ast.FuncDecl:
+ // Match if the target line is within the function declaration lines
+ // Actually, we want the definition line exactly, or close to it.
+ start := cf.fset.Position(decl.Pos()).Line
+ // The object.Pos() points to the name, not the 'func' keyword, usually.
+ namePos := cf.fset.Position(decl.Name.Pos()).Line
+
+ if namePos == targetLine || start == targetLine {
+ checkDoc(decl.Doc, decl.Name.Name)
+ }
+
+ case *ast.GenDecl:
+ // GenDecl covers `type X struct`, `var X`, `const X`
+ // The GenDecl doc applies to all specs inside it usually.
+
+ // If the GenDecl itself starts on the line (e.g. `type ( ...`)
+ // or if it contains our line.
+ start := cf.fset.Position(decl.Pos()).Line
+ end := cf.fset.Position(decl.End()).Line
+
+ if targetLine >= start && targetLine <= end {
+ // Check the top-level GenDecl doc (e.g. "// Experimental\n var ( ... )")
+ if decl.Doc != nil && experimentalRegex.MatchString(decl.Doc.Text()) {
+ found = true
+ if *debugMode {
+ log.Printf(" -> [MATCH] GenDecl experimental block around line %d", targetLine)
+ }
+ return false
+ }
+
+ // Check individual specs
+ for _, spec := range decl.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ if cf.fset.Position(s.Name.Pos()).Line == targetLine {
+ checkDoc(s.Doc, s.Name.Name)
+ }
+ case *ast.ValueSpec: // var/const
+ for _, name := range s.Names {
+ if cf.fset.Position(name.Pos()).Line == targetLine {
+ checkDoc(s.Doc, name.Name)
+ }
+ }
+ }
+ }
+ }
+ }
+ return true
+ })
+
+ return found
+}
diff --git a/tools/container-images/README.md b/tools/container-images/README.md
new file mode 100644
index 000000000000..dfbc748ffe6b
--- /dev/null
+++ b/tools/container-images/README.md
@@ -0,0 +1,10 @@
+# Container Images
+
+The container images defined in this directory are used to maintain
+depenendencies up to date. **These images are not used to build the project.**
+
+These images cause [Dependabot] to do a version bump. After that, a [GitHub
+action] will update other artifacts in the repository.
+
+[Dependabot]: ../../.github/dependabot.yml
+[GitHub action]: ../../.github/workflows/bump-devcontainer-version.yml
diff --git a/tools/container-images/devcontainer/Dockerfile b/tools/container-images/devcontainer/Dockerfile
new file mode 100644
index 000000000000..3496e71c69c3
--- /dev/null
+++ b/tools/container-images/devcontainer/Dockerfile
@@ -0,0 +1 @@
+FROM mcr.microsoft.com/devcontainers/go:dev-1.25-bookworm@sha256:c9a8c52ad9c962655ef43c9e333954b2a4d22c18b184479cefec2833ae02b92e
diff --git a/tools/etcd-dump-db/backend.go b/tools/etcd-dump-db/backend.go
index 875997ad2476..ef88fb0ba33a 100644
--- a/tools/etcd-dump-db/backend.go
+++ b/tools/etcd-dump-db/backend.go
@@ -20,6 +20,7 @@ import (
"path/filepath"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
bolt "go.etcd.io/bbolt"
"go.etcd.io/etcd/api/v3/authpb"
@@ -71,7 +72,7 @@ func defaultDecoder(k, v []byte) {
func keyDecoder(k, v []byte) {
rev := mvcc.BytesToBucketKey(k)
var kv mvccpb.KeyValue
- if err := kv.Unmarshal(v); err != nil {
+ if err := proto.Unmarshal(v, &kv); err != nil {
panic(err)
}
fmt.Printf("rev=%+v, value=[key %q | val %q | created %d | mod %d | ver %d]\n", rev, string(kv.Key), string(kv.Value), kv.CreateRevision, kv.ModRevision, kv.Version)
@@ -87,7 +88,7 @@ func bytesToLeaseID(bytes []byte) int64 {
func leaseDecoder(k, v []byte) {
leaseID := bytesToLeaseID(k)
var lpb leasepb.Lease
- if err := lpb.Unmarshal(v); err != nil {
+ if err := proto.Unmarshal(v, &lpb); err != nil {
panic(err)
}
fmt.Printf("lease ID=%016x, TTL=%ds, remaining TTL=%ds\n", leaseID, lpb.TTL, lpb.RemainingTTL)
@@ -104,7 +105,7 @@ func authDecoder(k, v []byte) {
func authRolesDecoder(_, v []byte) {
role := &authpb.Role{}
- err := role.Unmarshal(v)
+ err := proto.Unmarshal(v, role)
if err != nil {
panic(err)
}
@@ -113,7 +114,7 @@ func authRolesDecoder(_, v []byte) {
func authUsersDecoder(_, v []byte) {
user := &authpb.User{}
- err := user.Unmarshal(v)
+ err := proto.Unmarshal(v, user)
if err != nil {
panic(err)
}
diff --git a/tools/etcd-dump-db/main.go b/tools/etcd-dump-db/main.go
index 6be6d83ac739..530e64092732 100644
--- a/tools/etcd-dump-db/main.go
+++ b/tools/etcd-dump-db/main.go
@@ -24,6 +24,8 @@ import (
"time"
"github.com/spf13/cobra"
+
+ "go.etcd.io/etcd/client/pkg/v3/fileutil"
)
var (
@@ -85,7 +87,7 @@ func listBucketCommandFunc(_ *cobra.Command, args []string) {
if !strings.HasSuffix(dp, "db") {
dp = filepath.Join(snapDir(dp), "db")
}
- if !existFileOrDir(dp) {
+ if !fileutil.Exist(dp) {
log.Fatalf("%q does not exist", dp)
}
@@ -106,7 +108,7 @@ func iterateBucketCommandFunc(_ *cobra.Command, args []string) {
if !strings.HasSuffix(dp, "db") {
dp = filepath.Join(snapDir(dp), "db")
}
- if !existFileOrDir(dp) {
+ if !fileutil.Exist(dp) {
log.Fatalf("%q does not exist", dp)
}
bucket := args[1]
@@ -124,7 +126,7 @@ func scanKeysCommandFunc(_ *cobra.Command, args []string) {
if !strings.HasSuffix(dp, "db") {
dp = filepath.Join(snapDir(dp), "db")
}
- if !existFileOrDir(dp) {
+ if !fileutil.Exist(dp) {
log.Fatalf("%q does not exist", dp)
}
startRev, err := strconv.ParseInt(args[1], 10, 64)
@@ -145,7 +147,7 @@ func getHashCommandFunc(_ *cobra.Command, args []string) {
if !strings.HasSuffix(dp, "db") {
dp = filepath.Join(snapDir(dp), "db")
}
- if !existFileOrDir(dp) {
+ if !fileutil.Exist(dp) {
log.Fatalf("%q does not exist", dp)
}
diff --git a/tools/etcd-dump-db/scan.go b/tools/etcd-dump-db/scan.go
index 3898459d2608..838ccab11ac1 100644
--- a/tools/etcd-dump-db/scan.go
+++ b/tools/etcd-dump-db/scan.go
@@ -31,7 +31,7 @@ func scanKeys(dbPath string, startRev int64) error {
for pageID := uint64(2); pageID < hwm; {
p, _, err := readPage(dbPath, pgSize, pageID)
if err != nil {
- fmt.Fprintf(os.Stderr, "Reading page %d failed: %v. Continuting...\n", pageID, err)
+ fmt.Fprintf(os.Stderr, "Reading page %d failed: %v. Continuing...\n", pageID, err)
pageID++
continue
}
diff --git a/tools/etcd-dump-db/utils.go b/tools/etcd-dump-db/utils.go
index 184cb5181c47..35ba3d5c10cf 100644
--- a/tools/etcd-dump-db/utils.go
+++ b/tools/etcd-dump-db/utils.go
@@ -15,15 +15,9 @@
package main
import (
- "os"
"unsafe"
)
-func existFileOrDir(name string) bool {
- _, err := os.Stat(name)
- return err == nil
-}
-
func unsafeAdd(base unsafe.Pointer, offset uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(base) + offset)
}
diff --git a/tools/etcd-dump-logs/README.md b/tools/etcd-dump-logs/README.md
index 0922f2878fb1..58ed80df6d6c 100644
--- a/tools/etcd-dump-logs/README.md
+++ b/tools/etcd-dump-logs/README.md
@@ -52,7 +52,10 @@ Flags:
IRRRange, IRRPut, IRRDeleteRange, IRRTxn,
IRRCompaction, IRRLeaseGrant, IRRLeaseRevoke
-start-index uint
- The index to start dumping
+ The index to start dumping (inclusive)
+ If unspecified, dumps from the index of the last snapshot.
+ -end-index uint
+ The index to stop dumping (exclusive)
-start-snap string
The base name of snapshot file to start dumping
-stream-decoder string
@@ -146,11 +149,11 @@ Entry types () count is : 8
```
#### etcd-dump-logs -start-index [data dir]
-Only shows WAL log entries after the specified start-index number, exclusively.
+Only shows WAL log entries after the specified start-index number, inclusively.
```
-$ etcd-dump-logs -start-index 30 /tmp/datadir
-Start dumping log entries from index 30.
+$ etcd-dump-logs -start-index 31 /tmp/datadir
+Start dumping log entries from index 31.
WAL metadata:
nodeID=0 clusterID=0 term=0 commitIndex=0 vote=0
WAL entries:
@@ -162,4 +165,23 @@ term index type data
27 34 norm ???
Entry types () count is : 4
```
+
+#### etcd-dump-logs -start-index -end-index [data dir]
+
+Only shows WAL log entries from the specified start-index number (inclusively) to the specified end-index number (exclusively).
+
+```
+$ etcd-dump-logs -start-index 930 -end-index 932 /tmp/datadir
+Start dumping log entries from index 930.
+WAL metadata:
+nodeID=0 clusterID=0 term=5 commitIndex=2448 vote=0
+WAL entries: 2
+lastIndex=931
+term index type data
+ 3 930 norm header: put:
+ 3 931 norm header: put:
+
+Entry types (Normal,ConfigChange) count is : 2
+```
+
[decoder_correctoutputformat.sh]: ./testdecoder/decoder_correctoutputformat.sh
diff --git a/tools/etcd-dump-logs/etcd-dump-log_test.go b/tools/etcd-dump-logs/etcd-dump-log_test.go
index bb08ec11403e..fc1e84535fcd 100644
--- a/tools/etcd-dump-logs/etcd-dump-log_test.go
+++ b/tools/etcd-dump-logs/etcd-dump-log_test.go
@@ -74,7 +74,8 @@ func TestEtcdDumpLogEntryType(t *testing.T) {
{"decoder_wrongoutputformat", []string{"-stream-decoder", decoderWrongOutputFormat, p}, "expectedoutput/decoder_wrongoutputformat.output"},
}
- for _, argtest := range argtests {
+ for i := range argtests {
+ argtest := &argtests[i]
t.Run(argtest.name, func(t *testing.T) {
cmd := exec.Command(dumpLogsBinary, argtest.args...)
actual, err := cmd.CombinedOutput()
@@ -82,10 +83,10 @@ func TestEtcdDumpLogEntryType(t *testing.T) {
expected, err := os.ReadFile(path.Join(binDir, argtest.fileExpected))
require.NoError(t, err)
- assert.EqualValues(t, string(expected), string(actual))
+ assert.Equal(t, string(expected), string(actual))
// The output files contains a lot of trailing whitespaces... difficult to diagnose without printing them explicitly.
// TODO(ptabor): Get rid of the whitespaces both in code and the test-files.
- assert.EqualValues(t, strings.ReplaceAll(string(expected), " ", "_"), strings.ReplaceAll(string(actual), " ", "_"))
+ assert.Equal(t, strings.ReplaceAll(string(expected), " ", "_"), strings.ReplaceAll(string(actual), " ", "_"))
})
}
}
@@ -103,59 +104,36 @@ func mustCreateWALLog(t *testing.T, path string) {
err = os.Mkdir(snapdir, 0o744)
require.NoError(t, err)
- ents := make([]raftpb.Entry, 0)
+ ents := make([]*raftpb.Entry, 0)
// append entries into wal log
appendConfigChangeEnts(&ents)
- appendNormalRequestEnts(&ents)
appendNormalIRREnts(&ents)
appendUnknownNormalEnts(&ents)
// force commit newly appended entries
- err = w.Save(raftpb.HardState{}, ents)
+ err = w.Save(&raftpb.HardState{}, ents)
require.NoError(t, err)
w.Close()
}
-func appendConfigChangeEnts(ents *[]raftpb.Entry) {
+func appendConfigChangeEnts(ents *[]*raftpb.Entry) {
configChangeData := []raftpb.ConfChange{
- {ID: 1, Type: raftpb.ConfChangeAddNode, NodeID: 2, Context: []byte("")},
- {ID: 2, Type: raftpb.ConfChangeRemoveNode, NodeID: 2, Context: []byte("")},
- {ID: 3, Type: raftpb.ConfChangeUpdateNode, NodeID: 2, Context: []byte("")},
- {ID: 4, Type: raftpb.ConfChangeAddLearnerNode, NodeID: 3, Context: []byte("")},
+ {Id: new(uint64(1)), Type: raftpb.ConfChangeAddNode.Enum(), NodeId: new(uint64(2)), Context: []byte("")},
+ {Id: new(uint64(2)), Type: raftpb.ConfChangeRemoveNode.Enum(), NodeId: new(uint64(2)), Context: []byte("")},
+ {Id: new(uint64(3)), Type: raftpb.ConfChangeUpdateNode.Enum(), NodeId: new(uint64(2)), Context: []byte("")},
+ {Id: new(uint64(4)), Type: raftpb.ConfChangeAddLearnerNode.Enum(), NodeId: new(uint64(3)), Context: []byte("")},
}
- configChangeEntries := []raftpb.Entry{
- {Term: 1, Index: 1, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(&configChangeData[0])},
- {Term: 2, Index: 2, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(&configChangeData[1])},
- {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(&configChangeData[2])},
- {Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(&configChangeData[3])},
+ configChangeEntries := []*raftpb.Entry{
+ {Term: new(uint64(1)), Index: new(uint64(1)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(&configChangeData[0])},
+ {Term: new(uint64(2)), Index: new(uint64(2)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(&configChangeData[1])},
+ {Term: new(uint64(2)), Index: new(uint64(3)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(&configChangeData[2])},
+ {Term: new(uint64(2)), Index: new(uint64(4)), Type: raftpb.EntryConfChange.Enum(), Data: pbutil.MustMarshalMessage(&configChangeData[3])},
}
*ents = append(*ents, configChangeEntries...)
}
-func appendNormalRequestEnts(ents *[]raftpb.Entry) {
- a := true
- b := false
-
- requests := []etcdserverpb.Request{
- {ID: 0, Method: "", Path: "/path0", Val: "{\"hey\":\"ho\",\"hi\":[\"yo\"]}", Dir: true, PrevValue: "", PrevIndex: 0, PrevExist: &b, Expiration: 9, Wait: false, Since: 1, Recursive: false, Sorted: false, Quorum: false, Time: 1, Stream: false, Refresh: &b},
- {ID: 1, Method: methodQGet, Path: "/path1", Val: "{\"0\":\"1\",\"2\":[\"3\"]}", Dir: false, PrevValue: "", PrevIndex: 0, PrevExist: &b, Expiration: 9, Wait: false, Since: 1, Recursive: false, Sorted: false, Quorum: false, Time: 1, Stream: false, Refresh: &b},
- {ID: 2, Method: methodSync, Path: "/path2", Val: "{\"0\":\"1\",\"2\":[\"3\"]}", Dir: false, PrevValue: "", PrevIndex: 0, PrevExist: &b, Expiration: 2, Wait: false, Since: 1, Recursive: false, Sorted: false, Quorum: false, Time: 1, Stream: false, Refresh: &b},
- {ID: 3, Method: methodDelete, Path: "/path3", Val: "{\"hey\":\"ho\",\"hi\":[\"yo\"]}", Dir: false, PrevValue: "", PrevIndex: 0, PrevExist: &a, Expiration: 2, Wait: false, Since: 1, Recursive: false, Sorted: false, Quorum: false, Time: 1, Stream: false, Refresh: &b},
- {ID: 4, Method: methodRandom, Path: "/path4/superlong" + strings.Repeat("/path", 30), Val: "{\"hey\":\"ho\",\"hi\":[\"yo\"]}", Dir: false, PrevValue: "", PrevIndex: 0, PrevExist: &b, Expiration: 2, Wait: false, Since: 1, Recursive: false, Sorted: false, Quorum: false, Time: 1, Stream: false, Refresh: &b},
- }
-
- for i, request := range requests {
- var currentry raftpb.Entry
- currentry.Term = 3
- currentry.Index = uint64(i + 5)
- currentry.Type = raftpb.EntryNormal
- currentry.Data = pbutil.MustMarshal(&request)
- *ents = append(*ents, currentry)
- }
-}
-
-func appendNormalIRREnts(ents *[]raftpb.Entry) {
+func appendNormalIRREnts(ents *[]*raftpb.Entry) {
irrrange := &etcdserverpb.RangeRequest{Key: []byte("1"), RangeEnd: []byte("hi"), Limit: 6, Revision: 1, SortOrder: 1, SortTarget: 0, Serializable: false, KeysOnly: false, CountOnly: false, MinModRevision: 0, MaxModRevision: 20000, MinCreateRevision: 0, MaxCreateRevision: 20000}
irrput := &etcdserverpb.PutRequest{Key: []byte("foo1"), Value: []byte("bar1"), Lease: 1, PrevKv: false, IgnoreValue: false, IgnoreLease: true}
@@ -209,7 +187,7 @@ func appendNormalIRREnts(ents *[]raftpb.Entry) {
irrauthroleget := &etcdserverpb.AuthRoleGetRequest{Role: "role3"}
perm := &authpb.Permission{
- PermType: authpb.WRITE,
+ PermType: authpb.Permission_WRITE,
Key: []byte("Keys"),
RangeEnd: []byte("RangeEnd"),
}
@@ -218,7 +196,7 @@ func appendNormalIRREnts(ents *[]raftpb.Entry) {
irrauthrolerevokepermission := &etcdserverpb.AuthRoleRevokePermissionRequest{Role: "role3", Key: []byte("key"), RangeEnd: []byte("rangeend")}
- irrs := []etcdserverpb.InternalRaftRequest{
+ irrs := []*etcdserverpb.InternalRaftRequest{
{ID: 5, Range: irrrange},
{ID: 6, Put: irrput},
{ID: 7, DeleteRange: irrdeleterange},
@@ -247,19 +225,19 @@ func appendNormalIRREnts(ents *[]raftpb.Entry) {
for i, irr := range irrs {
var currentry raftpb.Entry
- currentry.Term = uint64(i + 4)
- currentry.Index = uint64(i + 10)
- currentry.Type = raftpb.EntryNormal
- currentry.Data = pbutil.MustMarshal(&irr)
- *ents = append(*ents, currentry)
+ currentry.Term = new(uint64(i + 4))
+ currentry.Index = new(uint64(i + 10))
+ currentry.Type = raftpb.EntryNormal.Enum()
+ currentry.Data = pbutil.MustMarshalMessage(irr)
+ *ents = append(*ents, ¤try)
}
}
-func appendUnknownNormalEnts(ents *[]raftpb.Entry) {
+func appendUnknownNormalEnts(ents *[]*raftpb.Entry) {
var currentry raftpb.Entry
- currentry.Term = 27
- currentry.Index = 34
- currentry.Type = raftpb.EntryNormal
+ currentry.Term = new(uint64(27))
+ currentry.Index = new(uint64(34))
+ currentry.Type = raftpb.EntryNormal.Enum()
currentry.Data = []byte("?")
- *ents = append(*ents, currentry)
+ *ents = append(*ents, ¤try)
}
diff --git a/tools/etcd-dump-logs/main.go b/tools/etcd-dump-logs/main.go
index f0bba446762a..c219e77a87c5 100644
--- a/tools/etcd-dump-logs/main.go
+++ b/tools/etcd-dump-logs/main.go
@@ -23,13 +23,14 @@ import (
"fmt"
"io"
"log"
+ "math"
"os"
"os/exec"
"path/filepath"
"strings"
- "time"
"go.uber.org/zap"
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/types"
@@ -51,7 +52,8 @@ const (
func main() {
snapfile := flag.String("start-snap", "", "The base name of snapshot file to start dumping")
waldir := flag.String("wal-dir", "", "If set, dumps WAL from the informed path, rather than following the standard 'data_dir/member/wal/' location")
- index := flag.Uint64("start-index", 0, "The index to start dumping")
+ startIndex := flag.Uint64("start-index", 0, "The index to start dumping (inclusive). If unspecified, dumps from the index of the last snapshot.")
+ endIndex := flag.Uint64("end-index", math.MaxUint64, "The index to stop dumping (exclusive)")
// Default entry types are Normal and ConfigChange
entrytype := flag.String("entry-type", defaultEntryTypes, `If set, filters output by entry type. Must be one or more than one of:
ConfigChange, Normal, Request, InternalRaftRequest,
@@ -70,7 +72,7 @@ and output a hex encoded line of binary for each input line`)
}
dataDir := flag.Args()[0]
- if *snapfile != "" && *index != 0 {
+ if *snapfile != "" && *startIndex != 0 {
log.Fatal("start-snap and start-index flags cannot be used together.")
}
@@ -82,7 +84,7 @@ and output a hex encoded line of binary for each input line`)
})
if !*raw {
- ents := readUsingReadAll(lg, startFromIndex, index, snapfile, dataDir, waldir)
+ ents := readUsingReadAll(lg, startFromIndex, startIndex, endIndex, snapfile, dataDir, waldir)
fmt.Printf("WAL entries: %d\n", len(ents))
if len(ents) > 0 {
@@ -107,20 +109,26 @@ and output a hex encoded line of binary for each input line`)
if wd == "" {
wd = walDir(dataDir)
}
- readRaw(index, wd, os.Stdout)
+ readRaw(startIndex, wd, os.Stdout)
}
}
-func readUsingReadAll(lg *zap.Logger, startFromIndex bool, index *uint64, snapfile *string, dataDir string, waldir *string) []raftpb.Entry {
+func readUsingReadAll(lg *zap.Logger, startFromIndex bool, startIndex *uint64, endIndex *uint64, snapfile *string, dataDir string, waldir *string) []*raftpb.Entry {
var (
walsnap walpb.Snapshot
snapshot *raftpb.Snapshot
err error
)
+ endAtIndex := *endIndex < math.MaxUint64
if startFromIndex {
- fmt.Printf("Start dumping log entries from index %d.\n", *index)
- walsnap.Index = *index
+ fmt.Printf("Start dumping log entries from index %d.\n", *startIndex)
+ // ReadAll() reads entries from the index after walsnap.Index, so we need to move walsnap.Index back one.
+ if *startIndex > 0 {
+ *startIndex--
+ }
+ index := *startIndex
+ walsnap.Index = &index
} else {
if *snapfile == "" {
ss := snap.New(lg, snapDir(dataDir))
@@ -131,7 +139,7 @@ func readUsingReadAll(lg *zap.Logger, startFromIndex bool, index *uint64, snapfi
switch {
case err == nil:
- walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
+ walsnap.Index, walsnap.Term = new(snapshot.Metadata.GetIndex()), new(snapshot.Metadata.GetTerm())
nodes := genIDSlice(snapshot.Metadata.ConfState.Voters)
confStateJSON, merr := json.Marshal(snapshot.Metadata.ConfState)
@@ -153,19 +161,36 @@ func readUsingReadAll(lg *zap.Logger, startFromIndex bool, index *uint64, snapfi
wd = walDir(dataDir)
}
- w, err := wal.OpenForRead(zap.NewExample(), wd, walsnap)
+ w, err := wal.OpenForRead(zap.NewExample(), wd, &walsnap)
if err != nil {
log.Fatalf("Failed opening WAL: %v", err)
}
wmetadata, state, ents, err := w.ReadAll()
w.Close()
if err != nil && (!startFromIndex || !errors.Is(err, wal.ErrSnapshotNotFound)) {
- log.Fatalf("Failed reading WAL: %v", err)
+ // ReadAll might return ErrSliceOutOfRange and the first series of entries if the server is offline for a while and receives a snapshot from leader.
+ // It is ok to ignore ErrSliceOutOfRange if just requesting a specific range of entries
+ if !endAtIndex || !errors.Is(err, wal.ErrSliceOutOfRange) {
+ log.Fatalf("Failed reading WAL: %v", err)
+ }
+ log.Printf("Failed reading all WAL: %v", err)
}
id, cid := parseWALMetadata(wmetadata)
- vid := types.ID(state.Vote)
+ vid := types.ID(*state.Vote)
fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n",
id, cid, state.Term, state.Commit, vid)
+ if endAtIndex {
+ entries := make([]*raftpb.Entry, 0)
+ for _, e := range ents {
+ // WAL might contain entries with e.Index >= *endIndex from prev term, then e.Index < *endIndex in the next term.
+ // We cannot break when e.Index >= *endIndex.
+ if *e.Index >= *endIndex {
+ continue
+ }
+ entries = append(entries, e)
+ }
+ return entries
+ }
return ents
}
@@ -175,9 +200,9 @@ func snapDir(dataDir string) string { return filepath.Join(dataDir, "member", "s
func parseWALMetadata(b []byte) (id, cid types.ID) {
var metadata etcdserverpb.Metadata
- pbutil.MustUnmarshal(&metadata, b)
- id = types.ID(metadata.NodeID)
- cid = types.ID(metadata.ClusterID)
+ pbutil.MustUnmarshalMessage(&metadata, b)
+ id = types.ID(metadata.GetNodeID())
+ cid = types.ID(metadata.GetClusterID())
return id, cid
}
@@ -189,89 +214,78 @@ func genIDSlice(a []uint64) []types.ID {
return ids
}
-// excerpt replaces middle part with ellipsis and returns a double-quoted
-// string safely escaped with Go syntax.
-func excerpt(str string, pre, suf int) string {
- if pre+suf > len(str) {
- return fmt.Sprintf("%q", str)
- }
- return fmt.Sprintf("%q...%q", str[:pre], str[len(str)-suf:])
-}
-
-type EntryFilter func(e raftpb.Entry) (bool, string)
+type EntryFilter func(e *raftpb.Entry) (bool, string)
// The 9 pass functions below takes the raftpb.Entry and return if the entry should be printed and the type of entry,
// the type of the entry will used in the following print function
-func passConfChange(entry raftpb.Entry) (bool, string) {
- return entry.Type == raftpb.EntryConfChange, "ConfigChange"
+func passConfChange(entry *raftpb.Entry) (bool, string) {
+ return *entry.Type == raftpb.EntryConfChange, "ConfigChange"
}
-func passInternalRaftRequest(entry raftpb.Entry) (bool, string) {
+func passInternalRaftRequest(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil, "InternalRaftRequest"
}
-func passUnknownNormal(entry raftpb.Entry) (bool, string) {
- var rr1 etcdserverpb.Request
+func passUnknownNormal(entry *raftpb.Entry) (bool, string) {
var rr2 etcdserverpb.InternalRaftRequest
- return (entry.Type == raftpb.EntryNormal) && (rr1.Unmarshal(entry.Data) != nil) && (rr2.Unmarshal(entry.Data) != nil), "UnknownNormal"
+ return (*entry.Type == raftpb.EntryNormal) && proto.Unmarshal(entry.Data, &rr2) != nil, "UnknownNormal"
}
-func passIRRRange(entry raftpb.Entry) (bool, string) {
+func passIRRRange(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Range != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.Range != nil, "InternalRaftRequest"
}
-func passIRRPut(entry raftpb.Entry) (bool, string) {
+func passIRRPut(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Put != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.Put != nil, "InternalRaftRequest"
}
-func passIRRDeleteRange(entry raftpb.Entry) (bool, string) {
+func passIRRDeleteRange(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.DeleteRange != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.DeleteRange != nil, "InternalRaftRequest"
}
-func passIRRTxn(entry raftpb.Entry) (bool, string) {
+func passIRRTxn(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Txn != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.Txn != nil, "InternalRaftRequest"
}
-func passIRRCompaction(entry raftpb.Entry) (bool, string) {
+func passIRRCompaction(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.Compaction != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.Compaction != nil, "InternalRaftRequest"
}
-func passIRRLeaseGrant(entry raftpb.Entry) (bool, string) {
+func passIRRLeaseGrant(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.LeaseGrant != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.LeaseGrant != nil, "InternalRaftRequest"
}
-func passIRRLeaseRevoke(entry raftpb.Entry) (bool, string) {
+func passIRRLeaseRevoke(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.LeaseRevoke != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.LeaseRevoke != nil, "InternalRaftRequest"
}
-func passIRRLeaseCheckpoint(entry raftpb.Entry) (bool, string) {
+func passIRRLeaseCheckpoint(entry *raftpb.Entry) (bool, string) {
var rr etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr.Unmarshal(entry.Data) == nil && rr.LeaseCheckpoint != nil, "InternalRaftRequest"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr) == nil && rr.LeaseCheckpoint != nil, "InternalRaftRequest"
}
-func passRequest(entry raftpb.Entry) (bool, string) {
- var rr1 etcdserverpb.Request
+func passRequest(entry *raftpb.Entry) (bool, string) {
var rr2 etcdserverpb.InternalRaftRequest
- return entry.Type == raftpb.EntryNormal && rr1.Unmarshal(entry.Data) == nil && rr2.Unmarshal(entry.Data) != nil, "Request"
+ return *entry.Type == raftpb.EntryNormal && proto.Unmarshal(entry.Data, &rr2) != nil, "Request"
}
-type EntryPrinter func(e raftpb.Entry)
+type EntryPrinter func(e *raftpb.Entry)
// The 4 print functions below print the entry format based on there types
// printInternalRaftRequest is used to print entry information for IRRRange, IRRPut,
// IRRDeleteRange and IRRTxn entries
-func printInternalRaftRequest(entry raftpb.Entry) {
+func printInternalRaftRequest(entry *raftpb.Entry) {
var rr etcdserverpb.InternalRaftRequest
- if err := rr.Unmarshal(entry.Data); err == nil {
+ if proto.Unmarshal(entry.Data, &rr) == nil {
// Ensure we don't log user password
if rr.AuthUserChangePassword != nil && rr.AuthUserChangePassword.Password != "" {
rr.AuthUserChangePassword.Password = ""
@@ -280,35 +294,18 @@ func printInternalRaftRequest(entry raftpb.Entry) {
}
}
-func printUnknownNormal(entry raftpb.Entry) {
+func printUnknownNormal(entry *raftpb.Entry) {
fmt.Printf("%4d\t%10d\tnorm\t???", entry.Term, entry.Index)
}
-func printConfChange(entry raftpb.Entry) {
+func printConfChange(entry *raftpb.Entry) {
fmt.Printf("%4d\t%10d", entry.Term, entry.Index)
fmt.Print("\tconf")
var r raftpb.ConfChange
- if err := r.Unmarshal(entry.Data); err != nil {
+ if err := proto.Unmarshal(entry.Data, &r); err != nil {
fmt.Print("\t???")
} else {
- fmt.Printf("\tmethod=%s id=%s", r.Type, types.ID(r.NodeID))
- }
-}
-
-func printRequest(entry raftpb.Entry) {
- var r etcdserverpb.Request
- if err := r.Unmarshal(entry.Data); err == nil {
- fmt.Printf("%4d\t%10d\tnorm", entry.Term, entry.Index)
- switch r.Method {
- case "":
- fmt.Print("\tnoop")
- case methodSync:
- fmt.Printf("\tmethod=SYNC time=%q", time.Unix(0, r.Time).UTC())
- case methodQGet, methodDelete:
- fmt.Printf("\tmethod=%s path=%s", r.Method, excerpt(r.Path, 64, 64))
- default:
- fmt.Printf("\tmethod=%s path=%s val=%s", r.Method, excerpt(r.Path, 64, 64), excerpt(r.Val, 128, 0))
- }
+ fmt.Printf("\tmethod=%s id=%s", *r.Type, types.ID(*r.NodeId))
}
}
@@ -350,11 +347,10 @@ IRRCompaction, IRRLeaseGrant, IRRLeaseRevoke, IRRLeaseCheckpoint`, et)
}
// listEntriesType filters and prints entries based on the entry-type flag,
-func listEntriesType(entrytype string, streamdecoder string, ents []raftpb.Entry) {
+func listEntriesType(entrytype string, streamdecoder string, ents []*raftpb.Entry) {
entryFilters := evaluateEntrytypeFlag(entrytype)
printerMap := map[string]EntryPrinter{
"InternalRaftRequest": printInternalRaftRequest,
- "Request": printRequest,
"ConfigChange": printConfChange,
"UnknownNormal": printUnknownNormal,
}
diff --git a/tools/etcd-dump-logs/raw.go b/tools/etcd-dump-logs/raw.go
index 1638d149df59..48d051d91550 100644
--- a/tools/etcd-dump-logs/raw.go
+++ b/tools/etcd-dump-logs/raw.go
@@ -21,6 +21,9 @@ import (
"log"
"os"
"path/filepath"
+ "strings"
+
+ "google.golang.org/protobuf/proto"
"go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/client/pkg/v3/fileutil"
@@ -63,8 +66,8 @@ func readRaw(fromIndex *uint64, waldir string, out io.Writer) {
crcDesync = true
}
printRec(&rec, fromIndex, out)
- if rec.Type == wal.CrcType {
- decoder.UpdateCRC(rec.Crc)
+ if rec.GetType() == wal.CrcType {
+ decoder.UpdateCRC(rec.GetCrc())
crcDesync = false
}
continue
@@ -82,31 +85,66 @@ func readRaw(fromIndex *uint64, waldir string, out io.Writer) {
}
func printRec(rec *walpb.Record, fromIndex *uint64, out io.Writer) {
- switch rec.Type {
+ switch rec.GetType() {
case wal.MetadataType:
var metadata etcdserverpb.Metadata
- pbutil.MustUnmarshal(&metadata, rec.Data)
+ pbutil.MustUnmarshalMessage(&metadata, rec.Data)
fmt.Fprintf(out, "Metadata: %s\n", metadata.String())
case wal.CrcType:
- fmt.Fprintf(out, "CRC: %d\n", rec.Crc)
+ fmt.Fprintf(out, "CRC: %d\n", rec.GetCrc())
case wal.EntryType:
e := wal.MustUnmarshalEntry(rec.Data)
- if fromIndex == nil || e.Index >= *fromIndex {
- fmt.Fprintf(out, "Entry: %s\n", e.String())
+ if fromIndex == nil || *e.Index >= *fromIndex {
+ // Format: Entry: Term:%d Index:%d Data:%q
+ typeStr := ""
+ if *e.Type != raftpb.EntryNormal {
+ typeStr = fmt.Sprintf(" Type:%s", e.Type.String())
+ }
+
+ // Helper to format data as octal escaped string to match the old v1 behavior:
+ var dataStr string
+ if len(e.Data) > 0 {
+ var buf strings.Builder
+ for _, b := range e.Data {
+ if b >= 32 && b < 127 && b != '"' && b != '\\' {
+ buf.WriteByte(b)
+ } else {
+ switch b {
+ case '\n':
+ buf.WriteString(`\n`)
+ case '\r':
+ buf.WriteString(`\r`)
+ case '\t':
+ buf.WriteString(`\t`)
+ case '"':
+ buf.WriteString(`\"`)
+ case '\\':
+ buf.WriteString(`\\`)
+ default:
+ fmt.Fprintf(&buf, "\\%03o", b)
+ }
+ }
+ }
+ dataStr = fmt.Sprintf(" Data:\"%s\" ", buf.String())
+ }
+
+ fmt.Fprintf(out, "Entry: Term:%d Index:%d%s%s\n", *e.Term, *e.Index, typeStr, dataStr)
}
case wal.SnapshotType:
var snap walpb.Snapshot
- pbutil.MustUnmarshal(&snap, rec.Data)
- if fromIndex == nil || snap.Index >= *fromIndex {
- fmt.Fprintf(out, "Snapshot: %s\n", snap.String())
+ pbutil.MustUnmarshalMessage(&snap, rec.Data)
+ if fromIndex == nil || snap.GetIndex() >= *fromIndex {
+ fmt.Fprintf(out, "Snapshot: index:%d term:%d\n", snap.GetIndex(), snap.GetTerm())
}
case wal.StateType:
var state raftpb.HardState
- pbutil.MustUnmarshal(&state, rec.Data)
- if fromIndex == nil || state.Commit >= *fromIndex {
+ if err := proto.Unmarshal(rec.Data, &state); err != nil {
+ log.Fatalf("Error unmarshaling hardstate: %v", err)
+ }
+ if fromIndex == nil || *state.Commit >= *fromIndex {
fmt.Fprintf(out, "HardState: %s\n", state.String())
}
default:
- log.Printf("Unexpected WAL log type: %d", rec.Type)
+ log.Printf("Unexpected WAL log type: %d", rec.GetType())
}
}
diff --git a/tools/etcd-dump-logs/raw_test.go b/tools/etcd-dump-logs/raw_test.go
index 3deb4bc390fc..7b8fe1a3e8e6 100644
--- a/tools/etcd-dump-logs/raw_test.go
+++ b/tools/etcd-dump-logs/raw_test.go
@@ -4,17 +4,19 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT 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 main
import (
"bytes"
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -25,19 +27,13 @@ func Test_readRaw(t *testing.T) {
mustCreateWALLog(t, path)
var out bytes.Buffer
readRaw(nil, walDir(path), &out)
- assert.Equal(t,
- `CRC: 0
+ assertReadRawOutput(t, `CRC: 0
Metadata:
-Snapshot:
+Snapshot: index:0 term:0
Entry: Term:1 Index:1 Type:EntryConfChange Data:"\010\001\020\000\030\002\"\000"
Entry: Term:2 Index:2 Type:EntryConfChange Data:"\010\002\020\001\030\002\"\000"
Entry: Term:2 Index:3 Type:EntryConfChange Data:"\010\003\020\002\030\002\"\000"
Entry: Term:2 Index:4 Type:EntryConfChange Data:"\010\004\020\003\030\003\"\000"
-Entry: Term:3 Index:5 Data:"\010\000\022\000\032\006/path0\"\030{\"hey\":\"ho\",\"hi\":[\"yo\"]}(\0012\0008\000@\000H\tP\000X\001`+"`"+`\000h\000p\000x\001\200\001\000\210\001\000"
-Entry: Term:3 Index:6 Data:"\010\001\022\004QGET\032\006/path1\"\023{\"0\":\"1\",\"2\":[\"3\"]}(\0002\0008\000@\000H\tP\000X\001`+"`"+`\000h\000p\000x\001\200\001\000\210\001\000"
-Entry: Term:3 Index:7 Data:"\010\002\022\004SYNC\032\006/path2\"\023{\"0\":\"1\",\"2\":[\"3\"]}(\0002\0008\000@\000H\002P\000X\001`+"`"+`\000h\000p\000x\001\200\001\000\210\001\000"
-Entry: Term:3 Index:8 Data:"\010\003\022\006DELETE\032\006/path3\"\030{\"hey\":\"ho\",\"hi\":[\"yo\"]}(\0002\0008\000@\001H\002P\000X\001`+"`"+`\000h\000p\000x\001\200\001\000\210\001\000"
-Entry: Term:3 Index:9 Data:"\010\004\022\006RANDOM\032\246\001/path4/superlong/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path/path\"\030{\"hey\":\"ho\",\"hi\":[\"yo\"]}(\0002\0008\000@\000H\002P\000X\001`+"`"+`\000h\000p\000x\001\200\001\000\210\001\000"
Entry: Term:4 Index:10 Data:"\010\005\032\025\n\0011\022\002hi\030\006 \001(\001X\240\234\001h\240\234\001"
Entry: Term:5 Index:11 Data:"\010\006\"\020\n\004foo1\022\004bar1\030\0010\001"
Entry: Term:6 Index:12 Data:"\010\007*\010\n\0010\022\0019\030\001"
@@ -66,3 +62,21 @@ Entry: Term:27 Index:34 Data:"?"
EOF: All entries were processed.
`, out.String())
}
+
+func assertReadRawOutput(t *testing.T, expected, actual string) {
+ t.Helper()
+
+ // google.golang.org/protobuf intentionally makes String output unstable across
+ // binaries by varying whitespace, so accept the alternate snapshot line.
+ // See https://github.com/protocolbuffers/protobuf-go/blob/v1.36.11/internal/encoding/text/encode.go#L229-L232.
+ expectedWithExtraSpace := strings.Replace(expected, readRawSnapshotLine, readRawSnapshotLineWithExtraSpace, 1)
+ if actual == expected || actual == expectedWithExtraSpace {
+ return
+ }
+ assert.Equal(t, expected, actual)
+}
+
+const (
+ readRawSnapshotLine = "Snapshot: index:0 term:0\n"
+ readRawSnapshotLineWithExtraSpace = "Snapshot: index:0 term:0\n"
+)
diff --git a/tools/etcd-dump-metrics/metrics.go b/tools/etcd-dump-metrics/metrics.go
index a1a40b18581d..f3332093b1a8 100644
--- a/tools/etcd-dump-metrics/metrics.go
+++ b/tools/etcd-dump-metrics/metrics.go
@@ -22,9 +22,9 @@ import (
"strings"
"time"
- "go.etcd.io/etcd/client/pkg/v3/transport"
-
"go.uber.org/zap"
+
+ "go.etcd.io/etcd/client/pkg/v3/transport"
)
func fetchMetrics(ep string) (lines []string, err error) {
diff --git a/tools/mod/go.mod b/tools/mod/go.mod
index b3482ddbebb6..25fb14e92199 100644
--- a/tools/mod/go.mod
+++ b/tools/mod/go.mod
@@ -1,35 +1,37 @@
module go.etcd.io/etcd/tools/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
- github.com/alexfalkowski/gocovmerge v1.3.18
+ github.com/alexfalkowski/gocovmerge v1.11.0
github.com/appscodelabs/license-bill-of-materials v0.0.0-20220707232035-6018e0c5287c
- github.com/chzchzchz/goword v0.0.0-20170907005317-a9744cb52b03
github.com/cloudflare/cfssl v1.6.5
- github.com/gogo/protobuf v1.3.2
- github.com/golangci/golangci-lint/v2 v2.8.0
- github.com/google/addlicense v1.1.1
- github.com/google/yamlfmt v0.15.0
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
+ github.com/golangci/golangci-lint/v2 v2.12.2
+ github.com/google/yamlfmt v0.21.0
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
+ github.com/ryancurrah/gomodguard v1.4.1
go.etcd.io/gofail v0.2.0
go.etcd.io/protodoc v0.0.0-20180829002748-484ab544e116
- go.etcd.io/raft/v3 v3.6.0
- golang.org/x/tools v0.42.0
- gotest.tools/gotestsum v1.12.0
- gotest.tools/v3 v3.5.1
- honnef.co/go/tools v0.6.1
+ go.etcd.io/raft/v3 v3.7.0
+ golang.org/x/tools v0.45.0
+ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2
+ google.golang.org/protobuf v1.36.11
+ gotest.tools/gotestsum v1.13.0
+ gotest.tools/v3 v3.5.2
+ honnef.co/go/tools v0.7.0
)
require (
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
4d63.com/gochecknoglobals v0.2.2 // indirect
+ charm.land/lipgloss/v2 v2.0.3 // indirect
codeberg.org/chavacava/garif v0.2.0 // indirect
codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
dev.gaijin.team/go/golib v0.6.0 // indirect
+ filippo.io/edwards25519 v1.1.1 // indirect
github.com/4meepo/tagalign v1.4.3 // indirect
github.com/Abirdcfly/dupword v0.1.7 // indirect
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
@@ -38,60 +40,61 @@ require (
github.com/Antonboom/nilnil v1.1.1 // indirect
github.com/Antonboom/testifylint v1.6.4 // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
+ github.com/ClickHouse/clickhouse-go-linter v1.2.0 // indirect
github.com/Djarvur/go-err113 v0.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.4.0 // indirect
- github.com/MirrexOne/unqueryvet v1.4.0 // indirect
+ github.com/Masterminds/semver/v3 v3.5.0 // indirect
+ github.com/MirrexOne/unqueryvet v1.5.4 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
- github.com/akhenakh/hunspellgo v0.0.0-20160221122622-9db38fa26e19 // indirect
- github.com/alecthomas/chroma/v2 v2.21.1 // indirect
+ github.com/alecthomas/chroma/v2 v2.24.1 // indirect
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
- github.com/alexkohler/prealloc v1.0.1 // indirect
+ github.com/alexkohler/prealloc v1.1.0 // indirect
github.com/alfatraining/structtag v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/alingse/nilnesserr v0.2.0 // indirect
- github.com/ashanbrown/forbidigo/v2 v2.3.0 // indirect
- github.com/ashanbrown/makezero/v2 v2.1.0 // indirect
- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
+ github.com/ashanbrown/forbidigo/v2 v2.3.1 // indirect
+ github.com/ashanbrown/makezero/v2 v2.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bitfield/gotestdox v0.2.2 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
- github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
github.com/bombsimon/wsl/v4 v4.7.0 // indirect
- github.com/bombsimon/wsl/v5 v5.3.0 // indirect
- github.com/braydonk/yaml v0.7.0 // indirect
+ github.com/bombsimon/wsl/v5 v5.8.0 // indirect
github.com/breml/bidichk v0.3.3 // indirect
github.com/breml/errchkjson v0.4.1 // indirect
- github.com/butuzov/ireturn v0.4.0 // indirect
+ github.com/butuzov/ireturn v0.4.1 // indirect
github.com/butuzov/mirror v1.3.0 // indirect
github.com/catenacyber/perfsprint v0.10.1 // indirect
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.11 // indirect
- github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
- github.com/charmbracelet/lipgloss v1.1.0 // indirect
- github.com/charmbracelet/x/ansi v0.8.0 // indirect
- github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
- github.com/charmbracelet/x/term v0.2.1 // indirect
+ github.com/charmbracelet/colorprofile v0.4.3 // indirect
+ github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect
+ github.com/charmbracelet/x/ansi v0.11.7 // indirect
+ github.com/charmbracelet/x/term v0.2.2 // indirect
+ github.com/charmbracelet/x/termios v0.1.1 // indirect
+ github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/ckaznocha/intrange v0.3.1 // indirect
+ github.com/clipperhouse/displaywidth v0.11.0 // indirect
+ github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/curioswitch/go-reassign v0.3.0 // indirect
github.com/daixiang0/gci v0.13.7 // indirect
github.com/dave/dst v0.27.3 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/denis-tingaikin/go-header v0.5.0 // indirect
- github.com/dlclark/regexp2 v1.11.5 // indirect
+ github.com/dlclark/regexp2 v1.12.0 // indirect
github.com/dnephin/pflag v1.0.7 // indirect
github.com/ettle/strcase v0.2.0 // indirect
- github.com/fatih/color v1.18.0 // indirect
+ github.com/fatih/color v1.19.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.6 // indirect
- github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
- github.com/ghostiam/protogetter v0.3.18 // indirect
+ github.com/ghostiam/protogetter v0.3.20 // indirect
github.com/go-critic/go-critic v0.14.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
- github.com/go-sql-driver/mysql v1.7.1 // indirect
+ github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-toolsmith/astcast v1.1.0 // indirect
github.com/go-toolsmith/astcopy v1.1.0 // indirect
github.com/go-toolsmith/astequal v1.2.0 // indirect
@@ -99,23 +102,23 @@ require (
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
- github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
+ github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
- github.com/godoc-lint/godoc-lint v0.11.1 // indirect
+ github.com/godoc-lint/godoc-lint v0.11.2 // indirect
github.com/gofrs/flock v0.13.0 // indirect
- github.com/golang/protobuf v1.5.4 // indirect
github.com/golangci/asciicheck v0.5.0 // indirect
- github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect
+ github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 // indirect
github.com/golangci/go-printf-func-name v0.1.1 // indirect
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect
- github.com/golangci/golines v0.14.0 // indirect
- github.com/golangci/misspell v0.7.0 // indirect
+ github.com/golangci/golines v0.15.0 // indirect
+ github.com/golangci/misspell v0.8.0 // indirect
github.com/golangci/plugin-module-register v0.1.2 // indirect
github.com/golangci/revgrep v0.8.0 // indirect
+ github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba // indirect
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
- github.com/google/certificate-transparency-go v1.1.7 // indirect
+ github.com/google/certificate-transparency-go v1.1.8 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gordonklaus/ineffassign v0.2.0 // indirect
@@ -124,19 +127,17 @@ require (
github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect
github.com/gostaticanalysis/nilerr v0.1.2 // indirect
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
- github.com/hashicorp/go-version v1.8.0 // indirect
+ github.com/hashicorp/go-version v1.9.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
- github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/jgautheron/goconst v1.8.2 // indirect
- github.com/jingyugao/rowserrcheck v1.1.1 // indirect
+ github.com/jgautheron/goconst v1.10.0 // indirect
github.com/jjti/go-spancheck v0.6.5 // indirect
github.com/jmhodges/clock v1.2.0 // indirect
- github.com/jmoiron/sqlx v1.3.5 // indirect
+ github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
- github.com/kisielk/errcheck v1.9.0 // indirect
+ github.com/kisielk/errcheck v1.10.0 // indirect
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 // indirect
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
github.com/kulti/thelper v0.7.1 // indirect
@@ -149,36 +150,36 @@ require (
github.com/ldez/tagliatelle v0.7.2 // indirect
github.com/ldez/usetesting v0.5.0 // indirect
github.com/leonklingele/grouper v1.1.2 // indirect
- github.com/lib/pq v1.10.9 // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/lib/pq v1.12.3 // indirect
+ github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
github.com/macabu/inamedparam v0.2.0 // indirect
- github.com/magiconair/properties v1.8.7 // indirect
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
- github.com/manuelarte/funcorder v0.5.0 // indirect
+ github.com/manuelarte/funcorder v0.6.0 // indirect
github.com/maratori/testableexamples v1.0.1 // indirect
github.com/maratori/testpackage v1.1.2 // indirect
github.com/matoous/godox v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/mattn/go-sqlite3 v1.14.22 // indirect
- github.com/mgechev/revive v1.13.0 // indirect
+ github.com/mattn/go-runewidth v0.0.23 // indirect
+ github.com/mattn/go-sqlite3 v1.14.24 // indirect
+ github.com/mgechev/revive v1.15.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
- github.com/muesli/termenv v0.16.0 // indirect
+ github.com/muesli/cancelreader v0.2.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nishanths/exhaustive v0.12.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
- github.com/nunnatsa/ginkgolinter v0.21.2 // indirect
+ github.com/nunnatsa/ginkgolinter v0.23.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
- github.com/pelletier/go-toml/v2 v2.2.4 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_golang v1.20.5 // indirect
- github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.62.0 // indirect
- github.com/prometheus/procfs v0.15.1 // indirect
+ github.com/pelletier/go-toml/v2 v2.3.1 // indirect
+ github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/client_golang v1.23.2 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.67.5 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
github.com/quasilyte/go-ruleguard v0.4.5 // indirect
github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect
github.com/quasilyte/gogrep v0.5.0 // indirect
@@ -187,73 +188,71 @@ require (
github.com/raeperd/recvcheck v0.2.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
- github.com/ryancurrah/gomodguard v1.4.1 // indirect
- github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
+ github.com/ryancurrah/gomodguard/v2 v2.1.3 // indirect
+ github.com/ryanrolds/sqlclosecheck v0.6.0 // indirect
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
+ github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
- github.com/securego/gosec/v2 v2.22.11 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/securego/gosec/v2 v2.26.1 // indirect
+ github.com/sirupsen/logrus v1.9.4 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
- github.com/sonatard/noctx v0.4.0 // indirect
- github.com/sourcegraph/go-diff v0.7.0 // indirect
+ github.com/sonatard/noctx v0.5.1 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/sourcegraph/go-diff v0.8.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
- github.com/spf13/cast v1.5.0 // indirect
+ github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/cobra v1.10.2 // indirect
- github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- github.com/spf13/viper v1.12.0 // indirect
+ github.com/spf13/viper v1.20.0 // indirect
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.11.1 // indirect
- github.com/subosito/gotenv v1.4.1 // indirect
- github.com/tetafro/godot v1.5.4 // indirect
- github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/tetafro/godot v1.5.6 // indirect
+ github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 // indirect
github.com/timonwong/loggercheck v0.11.0 // indirect
github.com/tomarrell/wrapcheck/v2 v2.12.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
- github.com/trustmaster/go-aspell v0.0.0-20200701131845-c2b1f55bec8f // indirect
github.com/ultraware/funlen v0.2.0 // indirect
github.com/ultraware/whitespace v0.2.0 // indirect
- github.com/uudashr/gocognit v1.2.0 // indirect
- github.com/uudashr/iface v1.4.1 // indirect
- github.com/weppos/publicsuffix-go v0.30.0 // indirect
+ github.com/uudashr/gocognit v1.2.1 // indirect
+ github.com/uudashr/iface v1.4.2 // indirect
+ github.com/weppos/publicsuffix-go v0.30.3-0.20240510084413-5f1d03393b3d // indirect
github.com/xen0n/gosmopolitan v1.3.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.3.0 // indirect
github.com/ykadowak/zerologlint v0.1.5 // indirect
- github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 // indirect
- github.com/zmap/zlint/v3 v3.5.0 // indirect
+ github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c // indirect
+ github.com/zmap/zlint/v3 v3.6.0 // indirect
gitlab.com/bosi/decorder v0.4.2 // indirect
go-simpler.org/musttag v0.14.0 // indirect
- go-simpler.org/sloglint v0.11.1 // indirect
- go.augendre.info/arangolint v0.3.1 // indirect
+ go-simpler.org/sloglint v0.12.0 // indirect
+ go.augendre.info/arangolint v0.4.0 // indirect
go.augendre.info/fatcontext v0.9.0 // indirect
- go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- go.uber.org/zap v1.27.0 // indirect
+ go.uber.org/zap v1.27.1 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
- golang.org/x/crypto v0.48.0 // indirect
- golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect
- golang.org/x/mod v0.33.0 // indirect
- golang.org/x/net v0.51.0 // indirect
+ golang.org/x/crypto v0.52.0 // indirect
+ golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect
+ golang.org/x/mod v0.36.0 // indirect
+ golang.org/x/net v0.55.0 // indirect
golang.org/x/sync v0.20.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect
- golang.org/x/term v0.40.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/grpc v1.79.3 // indirect
- google.golang.org/protobuf v1.36.10 // indirect
- gopkg.in/ini.v1 v1.67.0 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 // indirect
+ golang.org/x/term v0.43.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/grpc v1.81.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
- k8s.io/klog/v2 v2.100.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
mvdan.cc/gofumpt v0.9.2 // indirect
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect
+ sigs.k8s.io/yaml v1.6.0 // indirect
)
diff --git a/tools/mod/go.sum b/tools/mod/go.sum
index 8ecb007f0a8b..6116a478fb4a 100644
--- a/tools/mod/go.sum
+++ b/tools/mod/go.sum
@@ -2,6 +2,9 @@
4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
+charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU=
+charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA=
+cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY=
codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ=
codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI=
@@ -10,6 +13,9 @@ dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKf
dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI=
dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo=
dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
+filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8=
github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c=
github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ=
@@ -26,30 +32,31 @@ github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS8
github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/ClickHouse/clickhouse-go-linter v1.2.0 h1:zbm174up3hTKjp0wKZVnTzRiG7tSF5XZF0FJG/MuCBI=
+github.com/ClickHouse/clickhouse-go-linter v1.2.0/go.mod h1:pLorS7ffPTfuUV9M0SJgfHA/h/WQPQUk2FWG9x74cQ4=
github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g=
github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k=
-github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
-github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
-github.com/MirrexOne/unqueryvet v1.4.0 h1:6KAkqqW2KUnkl9Z0VuTphC3IXRPoFqEkJEtyxxHj5eQ=
-github.com/MirrexOne/unqueryvet v1.4.0/go.mod h1:IWwCwMQlSWjAIteW0t+28Q5vouyktfujzYznSIWiuOg=
+github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
+github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ=
+github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4=
github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo=
-github.com/akhenakh/hunspellgo v0.0.0-20160221122622-9db38fa26e19 h1:bYOD6QJnBJY79MJQR1i9cyQePG5oNDZXDKL2bhN/uvE=
-github.com/akhenakh/hunspellgo v0.0.0-20160221122622-9db38fa26e19/go.mod h1:HcqyLXmWoESd/vPSbCPqvgw5l5cMM5PtoqFOnXLjSeM=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
-github.com/alecthomas/chroma/v2 v2.21.1 h1:FaSDrp6N+3pphkNKU6HPCiYLgm8dbe5UXIXcoBhZSWA=
-github.com/alecthomas/chroma/v2 v2.21.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
+github.com/alecthomas/chroma/v2 v2.24.1 h1:m5ffpfZbIb++k8AqFEKy9uVgY12xIQtBsQlc6DfZJQM=
+github.com/alecthomas/chroma/v2 v2.24.1/go.mod h1:l+ohZ9xRXIbGe7cIW+YZgOGbvuVLjMps/FYN/CwuabI=
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
-github.com/alexfalkowski/gocovmerge v1.3.18 h1:GRJz7uNUHuumvWQtS2zBjDkLdIiCcYE1rfxYYWwYvs8=
-github.com/alexfalkowski/gocovmerge v1.3.18/go.mod h1:TnngCLiVe3kIrJ8v12zHP8aQkRWpjTomZm18uQrRDvE=
+github.com/alexfalkowski/gocovmerge v1.11.0 h1:mHYRBKEBHxjTWveV6RCAnCAhF6l1evO9JYboZRvZmuU=
+github.com/alexfalkowski/gocovmerge v1.11.0/go.mod h1:gkfERgPiozeXq7FlJBQDmTeXo8FnrNxZwabB4uSGZ3M=
github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ=
github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q=
-github.com/alexkohler/prealloc v1.0.1 h1:A9P1haqowqUxWvU9nk6tQ7YktXIHf+LQM9wPRhuteEE=
-github.com/alexkohler/prealloc v1.0.1/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig=
+github.com/alexkohler/prealloc v1.1.0 h1:cKGRBqlXw5iyQGLYhrXrDlcHxugXpTq4tQ5c91wkf8M=
+github.com/alexkohler/prealloc v1.1.0/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig=
github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc=
github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus=
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
@@ -58,12 +65,10 @@ github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEW
github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg=
github.com/appscodelabs/license-bill-of-materials v0.0.0-20220707232035-6018e0c5287c h1:xv0ICJ4AO52aNZ+vI2KFUYZBMh7dHvROixZ1vzMMfu8=
github.com/appscodelabs/license-bill-of-materials v0.0.0-20220707232035-6018e0c5287c/go.mod h1:Y5/1I+0gnnhHKyX4z65mgaGTJ08tnz9WUgkoymA/cws=
-github.com/ashanbrown/forbidigo/v2 v2.3.0 h1:OZZDOchCgsX5gvToVtEBoV2UWbFfI6RKQTir2UZzSxo=
-github.com/ashanbrown/forbidigo/v2 v2.3.0/go.mod h1:5p6VmsG5/1xx3E785W9fouMxIOkvY2rRV9nMdWadd6c=
-github.com/ashanbrown/makezero/v2 v2.1.0 h1:snuKYMbqosNokUKm+R6/+vOPs8yVAi46La7Ck6QYSaE=
-github.com/ashanbrown/makezero/v2 v2.1.0/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/ashanbrown/forbidigo/v2 v2.3.1 h1:KAZijvQ7zeIBKbhikT4jCm0TLYXC4u78bTiLh/8JROI=
+github.com/ashanbrown/forbidigo/v2 v2.3.1/go.mod h1:2QDkLTzU6TV937eFROamXrW92M3paehdae4HCDCOZCM=
+github.com/ashanbrown/makezero/v2 v2.2.1 h1:A7uU8dgB1PA9aelTxHMfHIQ8Qev8AB3JLxJUBUsejqM=
+github.com/ashanbrown/makezero/v2 v2.2.1/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE=
@@ -72,23 +77,21 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5
github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo=
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
-github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
-github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
-github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ=
github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg=
-github.com/bombsimon/wsl/v5 v5.3.0 h1:nZWREJFL6U3vgW/B1lfDOigl+tEF6qgs6dGGbFeR0UM=
-github.com/bombsimon/wsl/v5 v5.3.0/go.mod h1:Gp8lD04z27wm3FANIUPZycXp+8huVsn0oxc+n4qfV9I=
-github.com/braydonk/yaml v0.7.0 h1:ySkqO7r0MGoCNhiRJqE0Xe9yhINMyvOAB3nFjgyJn2k=
-github.com/braydonk/yaml v0.7.0/go.mod h1:hcm3h581tudlirk8XEUPDBAimBPbmnL0Y45hCRl47N4=
+github.com/bombsimon/wsl/v5 v5.8.0 h1:JTkyfs4yl8SPejrCF2GdABXE+mO1WvM7iUYzRWlsxDs=
+github.com/bombsimon/wsl/v5 v5.8.0/go.mod h1:AbOLsulgkqP4ZnitHf9gwPtCOGlrzkk0jb0uNxRSY0o=
github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE=
github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE=
github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg=
github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s=
-github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E=
-github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70=
+github.com/butuzov/ireturn v0.4.1 h1:vWb3NO4t77iku/sjCQ/2pHTQeOmxEhjIriJqRLg1Y+I=
+github.com/butuzov/ireturn v0.4.1/go.mod h1:q+DXKzTDV5guNuXLnIab9fKXizTn2miZHLhxH7V/GB4=
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
+github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ=
github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc=
github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
@@ -97,22 +100,27 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk=
github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
-github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
-github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
-github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
-github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
-github.com/chzchzchz/goword v0.0.0-20170907005317-a9744cb52b03 h1:0wUHjDfbCAROEAZ96zAJGwcNMkPIheFaIjtQyv3QqfM=
-github.com/chzchzchz/goword v0.0.0-20170907005317-a9744cb52b03/go.mod h1:uFE9hX+zXEwvyUThZ4gDb9vkAwc5DoHUnRSEpH0VrOs=
+github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
+github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
+github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 h1:OqDqxQZliC7C8adA7KjelW3OjtAxREfeHkNcd66wpeI=
+github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318/go.mod h1:Y6kE2GzHfkyQQVCSL9r2hwokSrIlHGzZG+71+wDYSZI=
+github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
+github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
+github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
+github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
+github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
+github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
+github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
+github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs=
github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk=
+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI=
github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4=
+github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -125,42 +133,39 @@ github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEy
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
-github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
-github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
+github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=
github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
-github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
-github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
-github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
+github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
-github.com/ghostiam/protogetter v0.3.18 h1:yEpghRGtP9PjKvVXtEzGpYfQj1Wl/ZehAfU6fr62Lfo=
-github.com/ghostiam/protogetter v0.3.18/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
+github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0=
+github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog=
github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ=
-github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
-github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
@@ -182,61 +187,60 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
-github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
-github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
+github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/godoc-lint/godoc-lint v0.11.1 h1:z9as8Qjiy6miRIa3VRymTa+Gt2RLnGICVikcvlUVOaA=
-github.com/godoc-lint/godoc-lint v0.11.1/go.mod h1:BAqayheFSuZrEAqCRxgw9MyvsM+S/hZwJbU1s/ejRj8=
+github.com/godoc-lint/godoc-lint v0.11.2 h1:Bp0FkJWoSdNsBikdNgIcgtaoo+xz6I/Y9s5WSBQUeeM=
+github.com/godoc-lint/godoc-lint v0.11.2/go.mod h1:iVpGdL1JCikNH2gGeAn3Hh+AgN5Gx/I/cxV+91L41jo=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
-github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0=
github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ=
-github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw=
-github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E=
+github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202 h1:CbTB8KpqnViI6lIXxp03Oclc4VFHi3K4BWC1TacsZ+A=
+github.com/golangci/dupl v0.0.0-20260401084720-c99c5cf5c202/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E=
github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U=
github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE=
github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY=
-github.com/golangci/golangci-lint/v2 v2.8.0 h1:wJnr3hJWY3eVzOUcfwbDc2qbi2RDEpvLmQeNFaPSNYA=
-github.com/golangci/golangci-lint/v2 v2.8.0/go.mod h1:xl+HafQ9xoP8rzw0z5AwnO5kynxtb80e8u02Ej/47RI=
-github.com/golangci/golines v0.14.0 h1:xt9d3RKBjhasA3qpoXs99J2xN2t6eBlpLHt0TrgyyXc=
-github.com/golangci/golines v0.14.0/go.mod h1:gf555vPG2Ia7mmy2mzmhVQbVjuK8Orw0maR1G4vVAAQ=
-github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c=
-github.com/golangci/misspell v0.7.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg=
+github.com/golangci/golangci-lint/v2 v2.12.2 h1:7+d1uY0bq1MU2UV3R5pW5Q7QWdcoq4naMRXM+gsJKrs=
+github.com/golangci/golangci-lint/v2 v2.12.2/go.mod h1:opqHHuIcTG2R+4akzWMd4o1BnD9/1LcjICWOujr91U8=
+github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0=
+github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10=
+github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg=
+github.com/golangci/misspell v0.8.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg=
github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg=
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
+github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba h1:lqtcnSMDuuJdu/LrKWi5RJzpSNLOJXYe/nzQutTI5kg=
+github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba/go.mod h1:sCBNcpRmhJCtbFGz49+IM3ETTFf7QdJ30AeYCd43NKk=
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM=
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s=
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM=
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc=
-github.com/google/addlicense v1.1.1 h1:jpVf9qPbU8rz5MxKo7d+RMcNHkqxi4YJi/laauX4aAE=
-github.com/google/addlicense v1.1.1/go.mod h1:Sm/DHu7Jk+T5miFHHehdIjbi4M5+dJDRS3Cq0rncIxA=
-github.com/google/certificate-transparency-go v1.1.7 h1:IASD+NtgSTJLPdzkthwvAG1ZVbF2WtFg4IvoA68XGSw=
-github.com/google/certificate-transparency-go v1.1.7/go.mod h1:FSSBo8fyMVgqptbfF6j5p/XNdgQftAhSmXcIxV9iphE=
+github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to=
+github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
-github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
+github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q=
+github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
+github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
-github.com/google/yamlfmt v0.15.0 h1:8VqeHp87EEyfAeCTp3QpV/8bnhDh04jKN6EeifiTM70=
-github.com/google/yamlfmt v0.15.0/go.mod h1:MPmHSVetV8ofpKmeiEZAh2p4jbDapC+FNqilNN7JQVk=
+github.com/google/yamlfmt v0.21.0 h1:9FKApQkDpMKgBjwLFytBHUCgqnQgxaQnci0uiESfbzs=
+github.com/google/yamlfmt v0.21.0/go.mod h1:q6FYExB+Ueu7jZDjKECJk+EaeDXJzJ6Ne0dxx69GWfI=
github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs=
github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw=
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
@@ -251,49 +255,40 @@ github.com/gostaticanalysis/nilerr v0.1.2/go.mod h1:A19UHhoY3y8ahoL7YKz6sdjDtduw
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8=
github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo=
github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
-github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
+github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4=
-github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako=
-github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
-github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
+github.com/jgautheron/goconst v1.10.0 h1:Ptt+OoE4NaEWKhLrWrrN3IpZdGLiqaf7WLnEX/iv4Jw=
+github.com/jgautheron/goconst v1.10.0/go.mod h1:0p+wv1lFOiUr0IlNNT1nrm6+8DB8u2sU6KHGzFRXHDc=
github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8=
github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU=
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
-github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
-github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
+github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
+github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0=
github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M=
-github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw=
+github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -321,19 +316,17 @@ github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc
github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ=
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
-github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
+github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
+github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
+github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE=
github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U=
-github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
-github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww=
github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM=
-github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8=
-github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA=
+github.com/manuelarte/funcorder v0.6.0 h1:0hBngc4fa1IgNiI65A7sFGkMvoMCc878RjqB5V7rWP0=
+github.com/manuelarte/funcorder v0.6.0/go.mod h1:id3NDhXdQBmeqXH7eVC6Z89xS6JxvZ8kF9xUxpArU/g=
github.com/maratori/testableexamples v1.0.1 h1:HfOQXs+XgfeRBJ+Wz0XfH+FHnoY9TVqL6Fcevpzy4q8=
github.com/maratori/testableexamples v1.0.1/go.mod h1:XE2F/nQs7B9N08JgyRmdGjYVGqxWwClLPCGSQhXQSrQ=
github.com/maratori/testpackage v1.1.2 h1:ffDSh+AgqluCLMXhM19f/cpvQAKygKAJXFl9aUjmbqs=
@@ -342,21 +335,17 @@ github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4=
github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
+github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
-github.com/mgechev/revive v1.13.0 h1:yFbEVliCVKRXY8UgwEO7EOYNopvjb1BFbmYqm9hZjBM=
-github.com/mgechev/revive v1.13.0/go.mod h1:efJfeBVCX2JUumNQ7dtOLDja+QKj9mYGgEZA7rt5u+0=
+github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
+github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q=
+github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -365,8 +354,8 @@ github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKH
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
-github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
-github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
+github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
+github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
@@ -375,12 +364,12 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
-github.com/nunnatsa/ginkgolinter v0.21.2 h1:khzWfm2/Br8ZemX8QM1pl72LwM+rMeW6VUbQ4rzh0Po=
-github.com/nunnatsa/ginkgolinter v0.21.2/go.mod h1:GItSI5fw7mCGLPmkvGYrr1kEetZe7B593jcyOpyabsY=
-github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
-github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
-github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
-github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
+github.com/nunnatsa/ginkgolinter v0.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8=
+github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4=
+github.com/onsi/ginkgo/v2 v2.28.2 h1:DTrMfpqxiNUyQ3Y0zhn1n3cOO2euFgQPYIpkWwxVFps=
+github.com/onsi/ginkgo/v2 v2.28.2/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
+github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
+github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
@@ -391,20 +380,21 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
-github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
+github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
+github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
-github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
-github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
-github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
+github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA=
github.com/quasilyte/go-ruleguard v0.4.5/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE=
github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
@@ -417,19 +407,21 @@ github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4l
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI=
github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g=
github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I=
-github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
-github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
+github.com/ryancurrah/gomodguard/v2 v2.1.3 h1:E7sz3PJwE9Ba1reVxSpF6XLCPJZ74Kfw/LabTNM4GIA=
+github.com/ryancurrah/gomodguard/v2 v2.1.3/go.mod h1:CQicdLGatWMxLX53JzoBjYlsNZhHbmLv2AVa0s2aivU=
+github.com/ryanrolds/sqlclosecheck v0.6.0 h1:pEyL9okISdg1F1SEpJNlrEotkTGerv5BMk7U4AG0eVg=
+github.com/ryanrolds/sqlclosecheck v0.6.0/go.mod h1:xyX16hsDaCMXHrMJ3JMzGf5OpDfHTOTTQrT7HOFUmeU=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
+github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
+github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
@@ -438,37 +430,35 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
-github.com/securego/gosec/v2 v2.22.11 h1:tW+weM/hCM/GX3iaCV91d5I6hqaRT2TPsFM1+USPXwg=
-github.com/securego/gosec/v2 v2.22.11/go.mod h1:KE4MW/eH0GLWztkbt4/7XpyH0zJBBnu7sYB4l6Wn7Mw=
+github.com/securego/gosec/v2 v2.26.1 h1:gdkttGhQFVehqRJ8grKH4DrpqM/QlPKNHBnl8QgcEC4=
+github.com/securego/gosec/v2 v2.26.1/go.mod h1:57UW4p0uoP3kxoTkhoo3axLdVAi+OWrLg/Ax/kdqtPE=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
-github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
-github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
-github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o=
-github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
-github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
-github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
+github.com/sonatard/noctx v0.5.1 h1:wklWg9c9ZYugOAk7qG4yP4PBrlQsmSLPTvW1K4PRQMs=
+github.com/sonatard/noctx v0.5.1/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/sourcegraph/go-diff v0.8.0 h1:ipIyu4cTsLbIrln4l0qtHA3r0a7gyK4ntKjtQytHhvY=
+github.com/sourcegraph/go-diff v0.8.0/go.mod h1:hWlcO7Al+UZStZAP8rBumHpCK5ZHQ5BXsMls8p4+F5E=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
-github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
+github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
+github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g=
@@ -484,37 +474,34 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
-github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
-github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg=
-github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU=
-github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk=
-github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460=
+github.com/tetafro/godot v1.5.6 h1:IEkrFCwXaYHlOn4mGzGS3F3dkP6m9t0jpwqBFPIkKiA=
+github.com/tetafro/godot v1.5.6/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU=
+github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 h1:SiHe5XLTn9sFWJ5pBwJ5FN/4j34q9ZlOAD//kMoMYp0=
+github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M=
github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M=
github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
github.com/tomarrell/wrapcheck/v2 v2.12.0 h1:H/qQ1aNWz/eeIhxKAFvkfIA+N7YDvq6TWVFL27Of9is=
github.com/tomarrell/wrapcheck/v2 v2.12.0/go.mod h1:AQhQuZd0p7b6rfW+vUwHm5OMCGgp63moQ9Qr/0BpIWo=
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
-github.com/trustmaster/go-aspell v0.0.0-20200701131845-c2b1f55bec8f h1:92ZQJRegaqnKjz9HY9an696Sw5EmAqRv0eie/U2IE6k=
-github.com/trustmaster/go-aspell v0.0.0-20200701131845-c2b1f55bec8f/go.mod h1:wxUiQ1klFJmwnM41kQI7IT2g8jjOKbtuL54LdjkxAI0=
github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI=
github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA=
github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g=
github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
-github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA=
-github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU=
-github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU=
-github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
-github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
+github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4=
+github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q=
+github.com/uudashr/iface v1.4.2 h1:06Vq5RKVYThBsj0Bnw4oasMjD1r+7CE/bcKOA8dVSvg=
+github.com/uudashr/iface v1.4.2/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
-github.com/weppos/publicsuffix-go v0.30.0 h1:QHPZ2GRu/YE7cvejH9iyavPOkVCB4dNxp2ZvtT+vQLY=
-github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY=
-github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
+github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222/go.mod h1:s41lQh6dIsDWIC1OWh7ChWJXLH0zkJ9KHZVqA7vHyuQ=
+github.com/weppos/publicsuffix-go v0.30.3-0.20240510084413-5f1d03393b3d h1:q80YKUcDWRNvvQcziH63e3ammTWARwrhohBCunHaYAg=
+github.com/weppos/publicsuffix-go v0.30.3-0.20240510084413-5f1d03393b3d/go.mod h1:vLdXKydr/OJssAXmjY0XBgLXUfivBMrNRIBljgtqCnw=
github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM=
github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
@@ -526,10 +513,8 @@ github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+
github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw=
github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
@@ -538,37 +523,37 @@ github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54t
github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk=
github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ=
github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ=
-github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 h1:DZH5n7L3L8RxKdSyJHZt7WePgwdhHnPhQFdQSJaHF+o=
-github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2fe2nV9KXsa9AyQBFZGzygVPovsZR+Rl5w=
+github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c h1:U1b4THKcgOpJ+kILupuznNwPiURtwVW3e9alJvji9+s=
+github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c/go.mod h1:GSDpFDD4TASObxvfZfvpZZ3OWHIUHMlhVWlkOe4ewVk=
github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8=
-github.com/zmap/zlint/v3 v3.5.0 h1:Eh2B5t6VKgVH0DFmTwOqE50POvyDhUaU9T2mJOe1vfQ=
-github.com/zmap/zlint/v3 v3.5.0/go.mod h1:JkNSrsDJ8F4VRtBZcYUQSvnWFL7utcjDIn+FE64mlBI=
+github.com/zmap/zlint/v3 v3.6.0 h1:vTEaDRtYN0d/1Ax60T+ypvbLQUHwHxbvYRnUMVr35ug=
+github.com/zmap/zlint/v3 v3.6.0/go.mod h1:NVgiIWssgzp0bNl8P4Gz94NHV2ep/4Jyj9V69uTmZyg=
gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo=
go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE=
-go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s=
-go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ=
-go.augendre.info/arangolint v0.3.1 h1:n2E6p8f+zfXSFLa2e2WqFPp4bfvcuRdd50y6cT65pSo=
-go.augendre.info/arangolint v0.3.1/go.mod h1:6ZKzEzIZuBQwoSvlKT+qpUfIbBfFCE5gbAoTg0/117g=
+go-simpler.org/sloglint v0.12.0 h1:UzWDlLWNE5FLqsvyq3tWYHuQMbqrervOhT8qPl4Mmw4=
+go-simpler.org/sloglint v0.12.0/go.mod h1:jBjjC2bm8rYrs88oTRlFX497kWjJsyZWYoNaXkGRI6I=
+go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50=
+go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA=
go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE=
go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw=
go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA=
go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o=
go.etcd.io/protodoc v0.0.0-20180829002748-484ab544e116 h1:QQiUXlqz+d96jyNG71NE+IGTgOK6Xlhdx+PzvfbLHlQ=
go.etcd.io/protodoc v0.0.0-20180829002748-484ab544e116/go.mod h1:F9kog+iVAuvPJucb1dkYcDcbV0g4uyGEHllTP5NrXiw=
-go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
-go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
-go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
-go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
+go.etcd.io/raft/v3 v3.7.0 h1:BGzlwx07bLv8PW6OU5HObuz1y4hlPZUXA07pM1mPUh4=
+go.etcd.io/raft/v3 v3.7.0/go.mod h1:6gX6T2X907DjnjsFLODnTxba77stjs84W9gTTI0GUNA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
+go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
+go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -578,60 +563,47 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
-golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
-golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 h1:HDjDiATsGqvuqvkDvgJjD1IgPrVekcSXVVE21JwvzGE=
-golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
+golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 h1:qWFG1Dj7TBjOjOvhEOkmyGPVoquqUKnIU0lEVLp8xyk=
+golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
-golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
+golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
+golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
-golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
+golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -639,9 +611,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -653,80 +622,62 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
-golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0=
-golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE=
+golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
-golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
-golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
-golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
+golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
-golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
-golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
-golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
-golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
-golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
+golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
+golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
@@ -735,36 +686,43 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
-google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 h1:rgSNvqscFZ1JgV/4wH5GOsZFSFkR2Eua9As3KIr2LlM=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2/go.mod h1:iMEtFwDlAhjDU9L5mY6U1XLwlIId/G3h+QcBHDIvrJ8=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
-gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gotest.tools/gotestsum v1.12.0 h1:CmwtaGDkHxrZm4Ib0Vob89MTfpc3GrEFMJKovliPwGk=
-gotest.tools/gotestsum v1.12.0/go.mod h1:fAvqkSptospfSbQw26CTYzNwnsE/ztqLeyhP0h67ARY=
-gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
-gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
-honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
-k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
-k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
+gotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo=
+gotest.tools/gotestsum v1.13.0/go.mod h1:7f0NS5hFb0dWr4NtcsAsF0y1kzjEFfAil0HiBQJE03Q=
+gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
+gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
+honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU=
+honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4=
mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s=
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI=
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/tools/mod/libs.go b/tools/mod/libs.go
index fd392d55a9f7..129a47f776b9 100644
--- a/tools/mod/libs.go
+++ b/tools/mod/libs.go
@@ -19,7 +19,3 @@
// for etcd. Thanks to this file 'go mod tidy' does not removes dependencies.
package libs
-
-import (
- _ "github.com/gogo/protobuf/proto"
-)
diff --git a/tools/mod/tools.go b/tools/mod/tools.go
index ca9e4649f3be..c5869ccdddd4 100644
--- a/tools/mod/tools.go
+++ b/tools/mod/tools.go
@@ -23,19 +23,21 @@ package tools
import (
_ "github.com/alexfalkowski/gocovmerge"
_ "github.com/appscodelabs/license-bill-of-materials"
- _ "github.com/chzchzchz/goword"
_ "github.com/cloudflare/cfssl/cmd/cfssl"
_ "github.com/cloudflare/cfssl/cmd/cfssljson"
_ "github.com/golangci/golangci-lint/v2/cmd/golangci-lint"
- _ "github.com/google/addlicense"
_ "github.com/google/yamlfmt/cmd/yamlfmt"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
- _ "go.etcd.io/gofail"
- _ "go.etcd.io/protodoc"
- _ "go.etcd.io/raft/v3"
+ _ "github.com/ryancurrah/gomodguard/cmd/gomodguard"
_ "golang.org/x/tools/cmd/goimports"
+ _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
+ _ "google.golang.org/protobuf/cmd/protoc-gen-go"
_ "gotest.tools/gotestsum"
_ "gotest.tools/v3"
_ "honnef.co/go/tools/cmd/staticcheck"
+
+ _ "go.etcd.io/gofail"
+ _ "go.etcd.io/protodoc"
+ _ "go.etcd.io/raft/v3"
)
diff --git a/tools/proto-annotations/cmd/etcd_version.go b/tools/proto-annotations/cmd/etcd_version.go
index 86766466adb8..893e726599ee 100644
--- a/tools/proto-annotations/cmd/etcd_version.go
+++ b/tools/proto-annotations/cmd/etcd_version.go
@@ -17,6 +17,7 @@ package cmd
import (
"fmt"
"io"
+ "slices"
"sort"
"strings"
@@ -72,10 +73,8 @@ func allEtcdVersionAnnotations() (annotations []etcdVersionAnnotation, err error
var fileAnnotations []etcdVersionAnnotation
protoregistry.GlobalFiles.RangeFiles(func(file protoreflect.FileDescriptor) bool {
pkg := string(file.Package())
- for _, externalPkg := range externalPackages {
- if pkg == externalPkg {
- return true
- }
+ if slices.Contains(externalPackages, pkg) {
+ return true
}
fileAnnotations, err = fileEtcdVersionAnnotations(file)
if err != nil {
diff --git a/tools/rw-heatmaps/cmd/root.go b/tools/rw-heatmaps/cmd/root.go
index 49b98a9fca29..722bddb20806 100644
--- a/tools/rw-heatmaps/cmd/root.go
+++ b/tools/rw-heatmaps/cmd/root.go
@@ -33,6 +33,8 @@ var (
ErrMissingInputFileArg = fmt.Errorf("missing input file argument")
// ErrInvalidOutputFormat is returned when the output format is invalid.
ErrInvalidOutputFormat = fmt.Errorf("invalid output format, must be one of png, jpg, jpeg, tiff")
+ // ErrInvalidChartTYpe is returned when the chart type is invalid.
+ ErrInvalidChartType = fmt.Errorf("invalid chart type, must be one of line, heatmap")
)
// NewRootCommand returns the root command for the rw-heatmaps tool.
@@ -56,6 +58,10 @@ func NewRootCommand() *cobra.Command {
}
}
+ if o.chartType == "line" {
+ return chart.PlotLineCharts(datasets, o.title, o.outputImageFile, o.outputFormat)
+ }
+
return chart.PlotHeatMaps(datasets, o.title, o.outputImageFile, o.outputFormat, o.zeroCentered)
},
}
@@ -70,6 +76,7 @@ type options struct {
outputImageFile string
outputFormat string
zeroCentered bool
+ chartType string
}
// newOptions returns a new options for the command with the default values applied.
@@ -77,6 +84,7 @@ func newOptions() options {
return options{
outputFormat: "jpg",
zeroCentered: true,
+ chartType: "heatmap",
}
}
@@ -86,6 +94,7 @@ func (o *options) AddFlags(fs *pflag.FlagSet) {
fs.StringVarP(&o.outputImageFile, "output-image-file", "o", o.outputImageFile, "output image filename (required)")
fs.StringVarP(&o.outputFormat, "output-format", "f", o.outputFormat, "output image file format")
fs.BoolVar(&o.zeroCentered, "zero-centered", o.zeroCentered, "plot the improvement graph with white color represents 0.0")
+ fs.StringVarP(&o.chartType, "chart-type", "c", o.chartType, `type of chart to plot ["line", or "heatmap"]`)
}
// Validate returns an error if the options are invalid.
@@ -101,5 +110,10 @@ func (o *options) Validate() error {
default:
return ErrInvalidOutputFormat
}
+ switch o.chartType {
+ case "line", "heatmap":
+ default:
+ return ErrInvalidChartType
+ }
return nil
}
diff --git a/tools/rw-heatmaps/go.mod b/tools/rw-heatmaps/go.mod
index 93533f8f347e..5cf44c3ee555 100644
--- a/tools/rw-heatmaps/go.mod
+++ b/tools/rw-heatmaps/go.mod
@@ -1,8 +1,8 @@
module go.etcd.io/etcd/tools/rw-heatmaps/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/spf13/cobra v1.10.2
@@ -19,8 +19,9 @@ require (
github.com/campoy/embedmd v1.0.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- golang.org/x/image v0.38.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- gonum.org/v1/gonum v0.16.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
+ golang.org/x/image v0.39.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ gonum.org/v1/gonum v0.17.0 // indirect
)
diff --git a/tools/rw-heatmaps/go.sum b/tools/rw-heatmaps/go.sum
index 31a155b3cdea..798999f3b79a 100644
--- a/tools/rw-heatmaps/go.sum
+++ b/tools/rw-heatmaps/go.sum
@@ -25,8 +25,9 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
@@ -38,10 +39,10 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
-golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
-golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
-golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
+golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
+golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
+golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -54,16 +55,16 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
gonum.org/v1/plot v0.15.2 h1:Tlfh/jBk2tqjLZ4/P8ZIwGrLEWQSPDLRm/SNWKNXiGI=
gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/tools/rw-heatmaps/pkg/chart/line_chart.go b/tools/rw-heatmaps/pkg/chart/line_chart.go
new file mode 100644
index 000000000000..b7421433cc61
--- /dev/null
+++ b/tools/rw-heatmaps/pkg/chart/line_chart.go
@@ -0,0 +1,226 @@
+// Copyright 2025 The etcd Authors
+//
+// 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 chart
+
+import (
+ "cmp"
+ "fmt"
+ "image/color"
+ "slices"
+ "sort"
+
+ "gonum.org/v1/plot"
+ "gonum.org/v1/plot/font"
+ "gonum.org/v1/plot/plotter"
+ "gonum.org/v1/plot/plotutil"
+ "gonum.org/v1/plot/vg"
+ "gonum.org/v1/plot/vg/draw"
+ "gonum.org/v1/plot/vg/vgimg"
+
+ "go.etcd.io/etcd/tools/rw-heatmaps/v3/pkg/dataset"
+)
+
+// PlotLineCharts creates a new line chart.
+func PlotLineCharts(datasets []*dataset.DataSet, title, outputImageFile, outputFormat string) error {
+ plot.DefaultFont = font.Font{
+ Typeface: "Liberation",
+ Variant: "Sans",
+ }
+
+ canvas := plotLineChart(datasets, title)
+ return saveCanvas(canvas, "readwrite", outputImageFile, outputFormat)
+}
+
+func plotLineChart(datasets []*dataset.DataSet, title string) *vgimg.Canvas {
+ ratiosLength := func() int {
+ max := slices.MaxFunc(datasets, func(a, b *dataset.DataSet) int {
+ return cmp.Compare(len(a.GetSortedRatios()), len(b.GetSortedRatios()))
+ })
+ return len(max.GetSortedRatios())
+ }()
+
+ // Make a nx1 grid of line charts.
+ const cols = 1
+ rows := ratiosLength
+
+ // Set the width and height of the canvas.
+ width, height := 30*vg.Centimeter, 15*font.Length(ratiosLength)*vg.Centimeter
+
+ canvas := vgimg.New(width, height)
+ dc := draw.New(canvas)
+
+ // Create a tiled layout for the plots.
+ t := draw.Tiles{
+ Rows: rows,
+ Cols: cols,
+ PadX: vg.Millimeter * 4,
+ PadY: vg.Millimeter * 4,
+ PadTop: vg.Millimeter * 15,
+ PadBottom: vg.Millimeter * 2,
+ PadLeft: vg.Millimeter * 2,
+ PadRight: vg.Millimeter * 2,
+ }
+
+ plots := make([][]*plot.Plot, rows)
+ legends := make([]plot.Legend, rows)
+ for i := range plots {
+ plots[i] = make([]*plot.Plot, cols)
+ }
+
+ // Load records into the grid.
+ ratios := slices.MaxFunc(datasets, func(a, b *dataset.DataSet) int {
+ return cmp.Compare(len(a.GetSortedRatios()), len(b.GetSortedRatios()))
+ }).GetSortedRatios()
+
+ for row, ratio := range ratios {
+ var records [][]dataset.DataRecord
+ var fileNames []string
+ for _, d := range datasets {
+ records = append(records, d.Records[ratio])
+ fileNames = append(fileNames, d.FileName)
+ }
+ p, l := plotIndividualLineChart(fmt.Sprintf("R/W Ratio %0.04f", ratio), records, fileNames)
+ plots[row] = []*plot.Plot{p}
+ legends[row] = l
+ }
+
+ // Fill the canvas with the plots and legends.
+ canvases := plot.Align(plots, t, dc)
+ for i := 0; i < rows; i++ {
+ // Continue if there is no plot in the current cell (incomplete data).
+ if plots[i][0] == nil {
+ continue
+ }
+
+ l := legends[i]
+ r := l.Rectangle(canvases[i][0])
+ legendWidth := r.Max.X - r.Min.X
+ // Adjust the legend down a little.
+ l.YOffs = plots[i][0].Title.TextStyle.FontExtents().Height * 3
+ l.Draw(canvases[i][0])
+
+ c := draw.Crop(canvases[i][0], 0, -legendWidth-vg.Millimeter, 0, 0)
+ plots[i][0].Draw(c)
+ }
+
+ // Add the title and parameter legend.
+ l := plot.NewLegend()
+ l.Add(title)
+ for _, d := range datasets {
+ l.Add(fmt.Sprintf("%s: %s", d.FileName, d.Param))
+ }
+ l.Top = true
+ l.Left = true
+ l.Draw(dc)
+
+ return canvas
+}
+
+func plotIndividualLineChart(title string, records [][]dataset.DataRecord, fileNames []string) (*plot.Plot, plot.Legend) {
+ p := plot.New()
+ p.Title.Text = title
+ p.X.Label.Text = "Connections Amount"
+ p.X.Scale = plot.LogScale{}
+ p.X.Tick.Marker = pow2Ticks{}
+ p.Y.Label.Text = "QPS (Requests/sec)"
+ p.Y.Scale = plot.LogScale{}
+ p.Y.Tick.Marker = pow2Ticks{}
+
+ legend := plot.NewLegend()
+
+ values := getSortedValueSizes(records...)
+ for i, rs := range records {
+ rec := make(map[int64][]dataset.DataRecord)
+ for _, r := range rs {
+ rec[r.ValueSize] = append(rec[r.ValueSize], r)
+ }
+ if len(records) > 1 {
+ addValues(p, &legend, values, rec, i, fileNames[i])
+ } else {
+ addValues(p, &legend, values, rec, i, "")
+ }
+ }
+
+ return p, legend
+}
+
+func getSortedValueSizes(records ...[]dataset.DataRecord) []int {
+ valueMap := make(map[int64]struct{})
+ for _, rs := range records {
+ for _, r := range rs {
+ valueMap[r.ValueSize] = struct{}{}
+ }
+ }
+
+ var values []int
+ for v := range valueMap {
+ values = append(values, int(v))
+ }
+ sort.Ints(values)
+
+ return values
+}
+
+func addValues(p *plot.Plot, legend *plot.Legend, values []int, rec map[int64][]dataset.DataRecord, index int, fileName string) {
+ for i, value := range values {
+ r := rec[int64(value)]
+ readPts := make(plotter.XYs, len(r))
+ writePts := make(plotter.XYs, len(r))
+ for i, record := range r {
+ writePts[i].X = float64(record.ConnSize)
+ readPts[i].X = writePts[i].X
+ readPts[i].Y = record.AvgRead
+ writePts[i].Y = record.AvgWrite
+ }
+
+ readLine, s, err := plotter.NewLinePoints(readPts)
+ if err != nil {
+ panic(err)
+ }
+ if index == 0 {
+ readLine.Color = plotutil.Color(0)
+ } else {
+ readLine.Color = plotutil.Color(2)
+ }
+ readLine.Width = vg.Length(vg.Millimeter * 0.15 * vg.Length(i+1))
+ readLine.Dashes = []vg.Length{vg.Points(6), vg.Points(2)}
+ s.Color = readLine.Color
+ p.Add(readLine, s)
+
+ writeLine, s, err := plotter.NewLinePoints(writePts)
+ if err != nil {
+ panic(err)
+ }
+ if index == 0 {
+ writeLine.Color = plotutil.Color(0)
+ } else {
+ writeLine.Color = plotutil.Color(2)
+ }
+ writeLine.Width = vg.Length(vg.Millimeter * 0.15 * vg.Length(i+1))
+ s.Color = writeLine.Color
+ p.Add(writeLine, s)
+
+ if index == 0 {
+ l, _, _ := plotter.NewLinePoints(writePts)
+ l.Color = color.RGBA{0, 0, 0, 255}
+ l.Width = vg.Length(vg.Millimeter * 0.15 * vg.Length(i+1))
+ legend.Add(fmt.Sprintf("%d", value), plot.Thumbnailer(l))
+ }
+ if i == len(values)-1 {
+ legend.Add(fmt.Sprintf("read %s", fileName), plot.Thumbnailer(readLine))
+ legend.Add(fmt.Sprintf("write %s", fileName), plot.Thumbnailer(writeLine))
+ }
+ }
+}
diff --git a/tools/testgrid-analysis/go.mod b/tools/testgrid-analysis/go.mod
index c3865e5042c6..e75c51aa0539 100644
--- a/tools/testgrid-analysis/go.mod
+++ b/tools/testgrid-analysis/go.mod
@@ -1,23 +1,23 @@
module go.etcd.io/etcd/tools/testgrid-analysis/v3
-go 1.25.0
+go 1.26
-toolchain go1.25.8
+toolchain go1.26.5
require (
github.com/GoogleCloudPlatform/testgrid v0.0.173
github.com/google/go-github/v60 v60.0.0
github.com/spf13/cobra v1.10.2
- google.golang.org/protobuf v1.36.10
+ google.golang.org/protobuf v1.36.11
)
require (
github.com/google/go-querystring v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
- golang.org/x/net v0.51.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
- google.golang.org/grpc v1.79.3 // indirect
+ golang.org/x/net v0.55.0 // indirect
+ golang.org/x/sys v0.45.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
+ google.golang.org/grpc v1.81.0 // indirect
)
diff --git a/tools/testgrid-analysis/go.sum b/tools/testgrid-analysis/go.sum
index 58eac912b9e0..a2aafc4aacb7 100644
--- a/tools/testgrid-analysis/go.sum
+++ b/tools/testgrid-analysis/go.sum
@@ -1184,16 +1184,16 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
-go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
-go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
@@ -1341,8 +1341,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1489,8 +1489,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1521,8 +1521,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1611,8 +1611,8 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
@@ -1848,8 +1848,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731190214-cbb8c96f2d6d/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1895,8 +1895,8 @@ google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGO
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
+google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
+google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1916,8 +1916,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=