Compare commits
154 Commits
pkg/v3.5.5
...
pkg/v3.5.8
Author | SHA1 | Date | |
---|---|---|---|
217d183e5a | |||
9d2cda4e44 | |||
3cd07fe17c | |||
cd019255ba | |||
643e6e1993 | |||
5872b80ed5 | |||
4501fd88c7 | |||
7f4eef09a3 | |||
b31caa3f12 | |||
b1df3df2b1 | |||
e6c2e380a9 | |||
291cb7172a | |||
070341c69f | |||
f905e2c264 | |||
e6eeca6885 | |||
65635e426c | |||
55bce22e97 | |||
7230b943d0 | |||
77baf66b52 | |||
1259884695 | |||
9e506593bf | |||
c5b670bff1 | |||
073c530989 | |||
6637aee804 | |||
c0421c7330 | |||
2d5f48a7ef | |||
a9e0a04c9a | |||
245067b15d | |||
63576a25f5 | |||
8b4549d534 | |||
f7ac9dfcd6 | |||
dcb1bf6078 | |||
44d78bf9aa | |||
fe37277c7e | |||
9e974792f9 | |||
15b3756abd | |||
92e56ab61e | |||
dafdaaedf2 | |||
930a450a55 | |||
86101d333b | |||
eb614c35f7 | |||
2eeb26083f | |||
00e1e5db21 | |||
46d6c1d7b2 | |||
2f4d75feb1 | |||
4e9911ec26 | |||
64bc55ef4e | |||
11ca1d356a | |||
358bcf3fb6 | |||
3a63da9609 | |||
55bfad950c | |||
3f96014d2d | |||
98861410f3 | |||
6ddb23ce7a | |||
e44995e670 | |||
b10adb6abe | |||
f0aa228dbf | |||
c602942246 | |||
1ea808b5ba | |||
183af509f6 | |||
1bd835383b | |||
5996b5faa3 | |||
d41d8ac1da | |||
e896cc7201 | |||
346f1d325a | |||
bc5445c00c | |||
452164a1c7 | |||
1664b4f828 | |||
b9568dba32 | |||
238da4b3f5 | |||
2a0ecd4078 | |||
78e739083e | |||
747de58414 | |||
41ed809c38 | |||
82004d0197 | |||
215b53cf3b | |||
638c6f1bb1 | |||
53300ece3b | |||
816c2e2b8a | |||
e1fc545d8a | |||
9e3966fbce | |||
cff304502c | |||
dd30268727 | |||
1293f5d4b7 | |||
f12f162587 | |||
cf379a79ea | |||
4e0385134e | |||
925c0611e0 | |||
413ec16175 | |||
bf1b902111 | |||
a612b9285f | |||
69ee8a83ab | |||
3337f35f17 | |||
b766840c3b | |||
410a987cbf | |||
d178292fec | |||
3b7248bc87 | |||
4d3c840a9b | |||
f1842b6ecf | |||
1fdfb4292c | |||
96ca27a3f2 | |||
127e9c05b0 | |||
c1a89973f0 | |||
0b47579263 | |||
22f599a2b7 | |||
378ad6b517 | |||
5454ca67bd | |||
ba122c9d56 | |||
cecbe35ce0 | |||
d0424a7bf1 | |||
1a9742c9c4 | |||
7ccca083eb | |||
c91978077b | |||
b2821631aa | |||
4097c24783 | |||
9849fa7c66 | |||
69aace20c8 | |||
5f387e6b7d | |||
563713e128 | |||
c2378be1b5 | |||
6797856841 | |||
cc6a082f9e | |||
27707209ae | |||
be4adc0c55 | |||
8902fe9246 | |||
45e31f6c80 | |||
8e26a1fff1 | |||
0a0f0e3617 | |||
bd7405a52e | |||
17cb291f15 | |||
1e96e0be38 | |||
efb9480b96 | |||
7cd9e5a338 | |||
d78f6f7f14 | |||
ec6f0a74ba | |||
62169d12eb | |||
d3da22fb1f | |||
acc7463fb2 | |||
2fb9be6f7d | |||
f6c4c84da3 | |||
3afd0735e0 | |||
e712234a1a | |||
3e195ba473 | |||
25ef9b6f46 | |||
5ff0d7fe26 | |||
dce3fdbeb1 | |||
07c7a98371 | |||
dd983c662b | |||
5daf35bb4a | |||
528dd82be9 | |||
7b568f23ab | |||
db55011d7c | |||
89d0fc49fc | |||
653d6e18c3 |
10
.github/workflows/e2e.yaml
vendored
10
.github/workflows/e2e.yaml
vendored
@ -13,20 +13,20 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16.15"
|
||||
go-version: "1.19.8"
|
||||
- run: date
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "${TARGET}"
|
||||
case "${TARGET}" in
|
||||
linux-amd64-e2e)
|
||||
PASSES='build release e2e' MANUAL_VER=v3.4.7 CPU='4' EXPECT_DEBUG='true' COVER='false' RACE='true' ./test.sh 2>&1 | tee test.log
|
||||
! egrep "(--- FAIL:|DATA RACE|panic: test timed out|appears to have leaked)" -B50 -A10 test.log
|
||||
PASSES='build release e2e' MANUAL_VER=v3.4.7 CPU='4' EXPECT_DEBUG='true' COVER='false' RACE='true' ./test.sh
|
||||
;;
|
||||
linux-386-e2e)
|
||||
GOARCH=386 PASSES='build e2e' CPU='4' EXPECT_DEBUG='true' COVER='false' RACE='true' ./test.sh 2>&1 | tee test.log
|
||||
! egrep "(--- FAIL:|DATA RACE|panic: test timed out|appears to have leaked)" -B50 -A10 test.log
|
||||
GOARCH=386 PASSES='build e2e' CPU='4' EXPECT_DEBUG='true' COVER='false' RACE='true' ./test.sh
|
||||
;;
|
||||
*)
|
||||
echo "Failed to find target"
|
||||
|
4
.github/workflows/functional.yaml
vendored
4
.github/workflows/functional.yaml
vendored
@ -12,11 +12,13 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16.15"
|
||||
go-version: "1.19.8"
|
||||
- run: date
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "${TARGET}"
|
||||
case "${TARGET}" in
|
||||
linux-amd64-functional)
|
||||
|
7
.github/workflows/grpcproxy.yaml
vendored
7
.github/workflows/grpcproxy.yaml
vendored
@ -12,16 +12,17 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16.15"
|
||||
go-version: "1.19.8"
|
||||
- run: date
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "${TARGET}"
|
||||
case "${TARGET}" in
|
||||
linux-amd64-grpcproxy)
|
||||
PASSES='build grpcproxy' CPU='4' COVER='false' RACE='true' ./test.sh 2>&1 | tee test.log
|
||||
! egrep "(--- FAIL:|DATA RACE|panic: test timed out|appears to have leaked)" -B50 -A10 test.log
|
||||
PASSES='build grpcproxy' CPU='4' COVER='false' RACE='true' ./test.sh
|
||||
;;
|
||||
*)
|
||||
echo "Failed to find target"
|
||||
|
12
.github/workflows/release.yaml
vendored
12
.github/workflows/release.yaml
vendored
@ -7,8 +7,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16.15"
|
||||
- run: |
|
||||
go-version: "1.19.8"
|
||||
- name: release
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
git config --global user.email "github-action@etcd.io"
|
||||
git config --global user.name "Github Action"
|
||||
gpg --batch --gen-key <<EOF
|
||||
@ -21,4 +24,7 @@ jobs:
|
||||
Name-Email: github-action@etcd.io
|
||||
Expire-Date: 0
|
||||
EOF
|
||||
DRY_RUN=true BRANCH=release-3.5 ./scripts/release --no-upload --no-docker-push 3.5.99
|
||||
DRY_RUN=true ./scripts/release --no-upload --no-docker-push --in-place 3.5.99
|
||||
- name: test-image
|
||||
run: |
|
||||
VERSION=3.5.99 ./scripts/test_images.sh
|
||||
|
4
.github/workflows/tests.yaml
vendored
4
.github/workflows/tests.yaml
vendored
@ -18,11 +18,13 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.16.15"
|
||||
go-version: "1.19.8"
|
||||
- run: date
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "${TARGET}"
|
||||
case "${TARGET}" in
|
||||
linux-amd64-fmt)
|
||||
|
37
.github/workflows/trivy-nightly-scan.yaml
vendored
Normal file
37
.github/workflows/trivy-nightly-scan.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Trivy Nightly Scan
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
|
||||
permissions: read-all
|
||||
jobs:
|
||||
nightly-scan:
|
||||
name: Trivy Scan nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# maintain the versions of etcd that need to be actively
|
||||
# security scanned
|
||||
versions: [v3.5.6]
|
||||
permissions:
|
||||
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
|
||||
with:
|
||||
ref: release-3.5
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 # master
|
||||
with:
|
||||
image-ref: 'gcr.io/etcd-development/etcd:${{ matrix.versions }}'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
format: 'template'
|
||||
template: '@/contrib/sarif.tpl'
|
||||
output: 'trivy-results-3-5.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2.1.36
|
||||
with:
|
||||
sarif_file: 'trivy-results-3-5.sarif'
|
51
.travis.yml
51
.travis.yml
@ -1,51 +0,0 @@
|
||||
language: go
|
||||
go_import_path: go.etcd.io/etcd/v3
|
||||
|
||||
sudo: required
|
||||
|
||||
services: docker
|
||||
|
||||
go:
|
||||
- "1.16.15"
|
||||
- tip
|
||||
|
||||
notifications:
|
||||
on_success: never
|
||||
on_failure: never
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- TARGET=linux-amd64-coverage
|
||||
- TARGET=linux-amd64-fmt-unit-go-tip-2-cpu
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: "1.16.15"
|
||||
env: TARGET=linux-amd64-coverage
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-fmt-unit-go-tip-2-cpu
|
||||
exclude:
|
||||
- go: tip
|
||||
env: TARGET=linux-amd64-coverage
|
||||
- go: "1.16.15"
|
||||
env: TARGET=linux-amd64-fmt-unit-go-tip-2-cpu
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_GO_VERSION == 1.* ]]; then docker pull gcr.io/etcd-development/etcd-test:go${TRAVIS_GO_VERSION}; fi
|
||||
|
||||
install:
|
||||
- date
|
||||
|
||||
script:
|
||||
- date
|
||||
- echo "TRAVIS_GO_VERSION=${TRAVIS_GO_VERSION}"
|
||||
- >
|
||||
case "${TARGET}" in
|
||||
linux-amd64-coverage)
|
||||
sudo HOST_TMP_DIR=/tmp TEST_OPTS="VERBOSE='1'" make docker-test-coverage
|
||||
;;
|
||||
linux-amd64-fmt-unit-go-tip-2-cpu)
|
||||
GOARCH=amd64 PASSES='fmt unit' CPU='2' RACE='false' ./test.sh -p=2
|
||||
;;
|
||||
esac
|
@ -1,17 +1,11 @@
|
||||
# TODO: move to k8s.gcr.io/build-image/debian-base:bullseye-v1.y.z when patched
|
||||
FROM debian:bullseye-20220328
|
||||
FROM --platform=linux/amd64 gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD etcdutl /usr/local/bin/
|
||||
RUN mkdir -p /var/etcd/
|
||||
RUN mkdir -p /var/lib/etcd/
|
||||
|
||||
# Alpine Linux doesn't use pam, which means that there is no /etc/nsswitch.conf,
|
||||
# but Golang relies on /etc/nsswitch.conf to check the order of DNS resolving
|
||||
# (see https://github.com/golang/go/commit/9dee7771f561cf6aee081c0af6658cc81fac3918)
|
||||
# To fix this we just create /etc/nsswitch.conf and add the following line:
|
||||
RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
# TODO: move to k8s.gcr.io/build-image/debian-base-arm64:bullseye-1.y.z when patched
|
||||
FROM arm64v8/debian:bullseye-20220328
|
||||
FROM --platform=linux/arm64 gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD etcdutl /usr/local/bin/
|
||||
ADD var/etcd /var/etcd
|
||||
ADD var/lib/etcd /var/lib/etcd
|
||||
ENV ETCD_UNSUPPORTED_ARCH=arm64
|
||||
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
# TODO: move to k8s.gcr.io/build-image/debian-base-ppc64le:bullseye-1.y.z when patched
|
||||
FROM ppc64le/debian:bullseye-20220328
|
||||
FROM --platform=linux/ppc64le gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD etcdutl /usr/local/bin/
|
||||
ADD var/etcd /var/etcd
|
||||
ADD var/lib/etcd /var/lib/etcd
|
||||
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
# TODO: move to k8s.gcr.io/build-image/debian-base-s390x:bullseye-1.y.z when patched
|
||||
FROM s390x/debian:bullseye-20220328
|
||||
FROM --platform=linux/s390x gcr.io/distroless/static-debian11
|
||||
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
ADD etcdutl /usr/local/bin/
|
||||
ADD var/etcd /var/etcd
|
||||
ADD var/lib/etcd /var/lib/etcd
|
||||
|
||||
WORKDIR /var/etcd/
|
||||
WORKDIR /var/lib/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -55,7 +55,7 @@ docker-remove:
|
||||
|
||||
|
||||
|
||||
GO_VERSION ?= 1.16.15
|
||||
GO_VERSION ?= 1.19.8
|
||||
ETCD_VERSION ?= $(shell git rev-parse --short HEAD || echo "GitNotFound")
|
||||
|
||||
TEST_SUFFIX = $(shell date +%s | base64 | head -c 15)
|
||||
@ -162,7 +162,7 @@ test-full:
|
||||
PASSES="fmt build release unit integration functional e2e grpcproxy" ./test.sh 2<&1 | tee test-$(TEST_SUFFIX).log
|
||||
|
||||
ensure-docker-test-image-exists:
|
||||
make pull-docker-test || echo "WARNING: Container Image not found in registry, building locally"; make build-docker-test
|
||||
make pull-docker-test || ( echo "WARNING: Container Image not found in registry, building locally"; make build-docker-test )
|
||||
|
||||
docker-test: ensure-docker-test-image-exists
|
||||
$(info GO_VERSION: $(GO_VERSION))
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
)
|
||||
|
||||
// TestInvalidGoYypeIntPanic tests conditions that caused
|
||||
// TestInvalidGoTypeIntPanic tests conditions that caused
|
||||
// panic: invalid Go type int for field k8s_io.kubernetes.vendor.go_etcd_io.etcd.etcdserver.etcdserverpb.loggablePutRequest.value_size
|
||||
// See https://github.com/kubernetes/kubernetes/issues/91937 for more details
|
||||
func TestInvalidGoTypeIntPanic(t *testing.T) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
module go.etcd.io/etcd/api/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
@ -9,6 +9,13 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c
|
||||
google.golang.org/grpc v1.41.0
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
var (
|
||||
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
|
||||
MinClusterVersion = "3.0.0"
|
||||
Version = "3.5.5"
|
||||
Version = "3.5.8"
|
||||
APIVersion = "unknown"
|
||||
|
||||
// Git SHA Value will be set during build
|
||||
|
@ -26,15 +26,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/certifi/gocertifi",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Mozilla Public License 2.0",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/cespare/xxhash/v2",
|
||||
"licenses": [
|
||||
@ -53,24 +44,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/cockroachdb/errors",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache License 2.0",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/cockroachdb/logtags",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache License 2.0",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/coreos/go-semver/semver",
|
||||
"licenses": [
|
||||
@ -134,24 +107,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/form3tech-oss/jwt-go",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 0.9891304347826086
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/getsentry/raven-go",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 3-clause \"New\" or \"Revised\" License",
|
||||
"confidence": 0.9663865546218487
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/gogo/protobuf",
|
||||
"licenses": [
|
||||
@ -161,6 +116,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/golang-jwt/jwt/v4",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT License",
|
||||
"confidence": 0.9891304347826086
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/golang/groupcache/lru",
|
||||
"licenses": [
|
||||
@ -296,15 +260,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/pkg/errors",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 2-clause \"Simplified\" License",
|
||||
"confidence": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "github.com/pmezard/go-difflib/difflib",
|
||||
"licenses": [
|
||||
@ -657,7 +612,16 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "golang.org/x/sys",
|
||||
"project": "golang.org/x/sync/errgroup",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 3-clause \"New\" or \"Revised\" License",
|
||||
"confidence": 0.9663865546218487
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"project": "golang.org/x/sys/unix",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD 3-clause \"New\" or \"Revised\" License",
|
||||
|
2
build
2
build
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo -e "\\e[91mDEPRECATED!!! Use build.sh script instead.\\e[0m\\n"
|
||||
sleep 1
|
||||
|
||||
|
40
build.sh
40
build.sh
@ -1,24 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source ./scripts/test_lib.sh
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD || echo "GitNotFound")
|
||||
if [[ -n "$FAILPOINTS" ]]; then
|
||||
if [[ -n "${FAILPOINTS:-}" ]]; then
|
||||
GIT_SHA="$GIT_SHA"-FAILPOINTS
|
||||
fi
|
||||
|
||||
VERSION_SYMBOL="${ROOT_MODULE}/api/v3/version.GitSHA"
|
||||
|
||||
# use go env if noset
|
||||
GOOS=${GOOS:-$(go env GOOS)}
|
||||
GOARCH=${GOARCH:-$(go env GOARCH)}
|
||||
|
||||
# Set GO_LDFLAGS="-s" for building without symbols for debugging.
|
||||
# shellcheck disable=SC2206
|
||||
GO_LDFLAGS=(${GO_LDFLAGS} "-X=${VERSION_SYMBOL}=${GIT_SHA}")
|
||||
GO_BUILD_ENV=("CGO_ENABLED=0" "GO_BUILD_FLAGS=${GO_BUILD_FLAGS}" "GOOS=${GOOS}" "GOARCH=${GOARCH}")
|
||||
GO_LDFLAGS=(${GO_LDFLAGS:-} "-X=${VERSION_SYMBOL}=${GIT_SHA}")
|
||||
GO_BUILD_ENV=("CGO_ENABLED=0" "GO_BUILD_FLAGS=${GO_BUILD_FLAGS:-}" "GOOS=${GOOS}" "GOARCH=${GOARCH}")
|
||||
|
||||
# enable/disable failpoints
|
||||
toggle_failpoints() {
|
||||
mode="$1"
|
||||
if command -v gofail >/dev/null 2>&1; then
|
||||
run gofail "$mode" server/etcdserver/ server/mvcc/backend/
|
||||
run gofail "$mode" server/etcdserver/ server/mvcc/backend/ server/wal/
|
||||
elif [[ "$mode" != "disable" ]]; then
|
||||
log_error "FAILPOINTS set but gofail not found"
|
||||
exit 1
|
||||
@ -27,13 +33,13 @@ toggle_failpoints() {
|
||||
|
||||
toggle_failpoints_default() {
|
||||
mode="disable"
|
||||
if [[ -n "$FAILPOINTS" ]]; then mode="enable"; fi
|
||||
if [[ -n "${FAILPOINTS:-}" ]]; then mode="enable"; fi
|
||||
toggle_failpoints "$mode"
|
||||
}
|
||||
|
||||
etcd_build() {
|
||||
out="bin"
|
||||
if [[ -n "${BINDIR}" ]]; then out="${BINDIR}"; fi
|
||||
if [[ -n "${BINDIR:-}" ]]; then out="${BINDIR}"; fi
|
||||
toggle_failpoints_default
|
||||
|
||||
run rm -f "${out}/etcd"
|
||||
@ -41,7 +47,8 @@ etcd_build() {
|
||||
cd ./server
|
||||
# Static compilation is useful when etcd is run in a container. $GO_BUILD_FLAGS is OK
|
||||
# shellcheck disable=SC2086
|
||||
run env "${GO_BUILD_ENV[@]}" go build $GO_BUILD_FLAGS \
|
||||
run env "${GO_BUILD_ENV[@]}" go build ${GO_BUILD_FLAGS:-} \
|
||||
-trimpath \
|
||||
-installsuffix=cgo \
|
||||
"-ldflags=${GO_LDFLAGS[*]}" \
|
||||
-o="../${out}/etcd" . || return 2
|
||||
@ -51,7 +58,8 @@ etcd_build() {
|
||||
# shellcheck disable=SC2086
|
||||
(
|
||||
cd ./etcdutl
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS}" "${GO_BUILD_ENV[@]}" go build $GO_BUILD_FLAGS \
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS:-}" "${GO_BUILD_ENV[@]}" go build ${GO_BUILD_FLAGS:-} \
|
||||
-trimpath \
|
||||
-installsuffix=cgo \
|
||||
"-ldflags=${GO_LDFLAGS[*]}" \
|
||||
-o="../${out}/etcdutl" . || return 2
|
||||
@ -61,7 +69,8 @@ etcd_build() {
|
||||
# shellcheck disable=SC2086
|
||||
(
|
||||
cd ./etcdctl
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS}" "${GO_BUILD_ENV[@]}" go build $GO_BUILD_FLAGS \
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS:-}" "${GO_BUILD_ENV[@]}" go build ${GO_BUILD_FLAGS:-} \
|
||||
-trimpath \
|
||||
-installsuffix=cgo \
|
||||
"-ldflags=${GO_LDFLAGS[*]}" \
|
||||
-o="../${out}/etcdctl" . || return 2
|
||||
@ -81,7 +90,7 @@ etcd_build() {
|
||||
|
||||
tools_build() {
|
||||
out="bin"
|
||||
if [[ -n "${BINDIR}" ]]; then out="${BINDIR}"; fi
|
||||
if [[ -n "${BINDIR:-}" ]]; then out="${BINDIR}"; fi
|
||||
tools_path="tools/benchmark
|
||||
tools/etcd-dump-db
|
||||
tools/etcd-dump-logs
|
||||
@ -91,9 +100,10 @@ tools_build() {
|
||||
echo "Building" "'${tool}'"...
|
||||
run rm -f "${out}/${tool}"
|
||||
# shellcheck disable=SC2086
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS}" CGO_ENABLED=0 go build ${GO_BUILD_FLAGS} \
|
||||
run env GO_BUILD_FLAGS="${GO_BUILD_FLAGS:-}" CGO_ENABLED=0 go build ${GO_BUILD_FLAGS:-} \
|
||||
-trimpath \
|
||||
-installsuffix=cgo \
|
||||
"-ldflags='${GO_LDFLAGS[*]}'" \
|
||||
"-ldflags=${GO_LDFLAGS[*]}" \
|
||||
-o="${out}/${tool}" "./${tool}" || return 2
|
||||
done
|
||||
tests_build "${@}"
|
||||
@ -101,7 +111,7 @@ tools_build() {
|
||||
|
||||
tests_build() {
|
||||
out="bin"
|
||||
if [[ -n "${BINDIR}" ]]; then out="${BINDIR}"; fi
|
||||
if [[ -n "${BINDIR:-}" ]]; then out="${BINDIR}"; fi
|
||||
tools_path="
|
||||
functional/cmd/etcd-agent
|
||||
functional/cmd/etcd-proxy
|
||||
@ -114,9 +124,9 @@ tests_build() {
|
||||
run rm -f "../${out}/${tool}"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
run env CGO_ENABLED=0 GO_BUILD_FLAGS="${GO_BUILD_FLAGS}" go build ${GO_BUILD_FLAGS} \
|
||||
run env CGO_ENABLED=0 GO_BUILD_FLAGS="${GO_BUILD_FLAGS:-}" go build ${GO_BUILD_FLAGS:-} \
|
||||
-installsuffix=cgo \
|
||||
"-ldflags='${GO_LDFLAGS[*]}'" \
|
||||
"-ldflags=${GO_LDFLAGS[*]}" \
|
||||
-o="../${out}/${tool}" "./${tool}" || return 2
|
||||
done
|
||||
) || return 2
|
||||
|
@ -44,16 +44,12 @@ func IsDirWriteable(dir string) error {
|
||||
|
||||
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
|
||||
// does not exists. TouchDirAll also ensures the given directory is writable.
|
||||
func TouchDirAll(dir string) error {
|
||||
func TouchDirAll(lg *zap.Logger, dir string) error {
|
||||
// If path is already a directory, MkdirAll does nothing and returns nil, so,
|
||||
// first check if dir exist with an expected permission mode.
|
||||
if Exist(dir) {
|
||||
err := CheckDirPermission(dir, PrivateDirMode)
|
||||
if err != nil {
|
||||
lg, _ := zap.NewProduction()
|
||||
if lg == nil {
|
||||
lg = zap.NewExample()
|
||||
}
|
||||
lg.Warn("check file permission", zap.Error(err))
|
||||
}
|
||||
} else {
|
||||
@ -70,8 +66,8 @@ func TouchDirAll(dir string) error {
|
||||
|
||||
// CreateDirAll is similar to TouchDirAll but returns error
|
||||
// if the deepest directory was not empty.
|
||||
func CreateDirAll(dir string) error {
|
||||
err := TouchDirAll(dir)
|
||||
func CreateDirAll(lg *zap.Logger, dir string) error {
|
||||
err := TouchDirAll(lg, dir)
|
||||
if err == nil {
|
||||
var ns []string
|
||||
ns, err = ReadDir(dir)
|
||||
|
@ -67,7 +67,7 @@ func TestCreateDirAll(t *testing.T) {
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
tmpdir2 := filepath.Join(tmpdir, "testdir")
|
||||
if err = CreateDirAll(tmpdir2); err != nil {
|
||||
if err = CreateDirAll(zaptest.NewLogger(t), tmpdir2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ func TestCreateDirAll(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = CreateDirAll(tmpdir2); err == nil || !strings.Contains(err.Error(), "to be empty, got") {
|
||||
if err = CreateDirAll(zaptest.NewLogger(t), tmpdir2); err == nil || !strings.Contains(err.Error(), "to be empty, got") {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
@ -186,7 +186,7 @@ func TestDirPermission(t *testing.T) {
|
||||
|
||||
tmpdir2 := filepath.Join(tmpdir, "testpermission")
|
||||
// create a new dir with 0700
|
||||
if err = CreateDirAll(tmpdir2); err != nil {
|
||||
if err = CreateDirAll(zaptest.NewLogger(t), tmpdir2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// check dir permission with mode different than created dir
|
||||
|
@ -41,6 +41,12 @@ func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval
|
||||
lg = zap.NewNop()
|
||||
}
|
||||
errC := make(chan error, 1)
|
||||
lg.Info("started to purge file",
|
||||
zap.String("dir", dirname),
|
||||
zap.String("suffix", suffix),
|
||||
zap.Uint("max", max),
|
||||
zap.Duration("interval", interval))
|
||||
|
||||
go func() {
|
||||
if donec != nil {
|
||||
defer close(donec)
|
||||
@ -63,14 +69,16 @@ func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval
|
||||
f := filepath.Join(dirname, newfnames[0])
|
||||
l, err := TryLockFile(f, os.O_WRONLY, PrivateFileMode)
|
||||
if err != nil {
|
||||
lg.Warn("failed to lock file", zap.String("path", f), zap.Error(err))
|
||||
break
|
||||
}
|
||||
if err = os.Remove(f); err != nil {
|
||||
lg.Error("failed to remove file", zap.String("path", f), zap.Error(err))
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
if err = l.Close(); err != nil {
|
||||
lg.Warn("failed to unlock/close", zap.String("path", l.Name()), zap.Error(err))
|
||||
lg.Error("failed to unlock/close", zap.String("path", l.Name()), zap.Error(err))
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
|
@ -1,10 +1,18 @@
|
||||
module go.etcd.io/etcd/client/pkg/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/coreos/go-systemd/v22 v22.3.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
@ -9,9 +9,14 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
@ -25,5 +30,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=
|
||||
|
@ -16,6 +16,7 @@ package logutil
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
@ -46,15 +47,20 @@ var DefaultZapLoggerConfig = zap.Config{
|
||||
|
||||
// copied from "zap.NewProductionEncoderConfig" with some updates
|
||||
EncoderConfig: zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
EncodeTime: zapcore.ISO8601TimeEncoder,
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
|
||||
// Custom EncodeTime function to ensure we match format and precision of historic capnslog timestamps
|
||||
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString(t.Format("2006-01-02T15:04:05.999999Z0700"))
|
||||
},
|
||||
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
},
|
||||
|
@ -14,7 +14,10 @@
|
||||
|
||||
package tlsutil
|
||||
|
||||
import "crypto/tls"
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetCipherSuite returns the corresponding cipher suite,
|
||||
// and boolean value if it is supported.
|
||||
@ -37,3 +40,17 @@ func GetCipherSuite(s string) (uint16, bool) {
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetCipherSuites returns list of corresponding cipher suite IDs.
|
||||
func GetCipherSuites(ss []string) ([]uint16, error) {
|
||||
cs := make([]uint16, len(ss))
|
||||
for i, s := range ss {
|
||||
var ok bool
|
||||
cs[i], ok = GetCipherSuite(s)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected TLS cipher suite %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
47
client/pkg/tlsutil/versions.go
Normal file
47
client/pkg/tlsutil/versions.go
Normal file
@ -0,0 +1,47 @@
|
||||
// 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 tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TLSVersion string
|
||||
|
||||
// Constants for TLS versions.
|
||||
const (
|
||||
TLSVersionDefault TLSVersion = ""
|
||||
TLSVersion12 TLSVersion = "TLS1.2"
|
||||
TLSVersion13 TLSVersion = "TLS1.3"
|
||||
)
|
||||
|
||||
// GetTLSVersion returns the corresponding tls.Version or error.
|
||||
func GetTLSVersion(version string) (uint16, error) {
|
||||
var v uint16
|
||||
|
||||
switch version {
|
||||
case string(TLSVersionDefault):
|
||||
v = 0 // 0 means let Go decide.
|
||||
case string(TLSVersion12):
|
||||
v = tls.VersionTLS12
|
||||
case string(TLSVersion13):
|
||||
v = tls.VersionTLS13
|
||||
default:
|
||||
return 0, fmt.Errorf("unexpected TLS version %q (must be one of: TLS1.2, TLS1.3)", version)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
63
client/pkg/tlsutil/versions_test.go
Normal file
63
client/pkg/tlsutil/versions_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
// 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 tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
want uint16
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "TLS1.2",
|
||||
version: "TLS1.2",
|
||||
want: tls.VersionTLS12,
|
||||
},
|
||||
{
|
||||
name: "TLS1.3",
|
||||
version: "TLS1.3",
|
||||
want: tls.VersionTLS13,
|
||||
},
|
||||
{
|
||||
name: "Empty version",
|
||||
version: "",
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "Converting invalid version string to TLS version",
|
||||
version: "not_existing",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := GetTLSVersion(tt.version)
|
||||
if err != nil {
|
||||
assert.True(t, tt.expectError, "GetTLSVersion() returned error while expecting success: %v", err)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
@ -165,6 +165,14 @@ type TLSInfo struct {
|
||||
// Note that cipher suites are prioritized in the given order.
|
||||
CipherSuites []uint16
|
||||
|
||||
// MinVersion is the minimum TLS version that is acceptable.
|
||||
// If not set, the minimum version is TLS 1.2.
|
||||
MinVersion uint16
|
||||
|
||||
// MaxVersion is the maximum TLS version that is acceptable.
|
||||
// If not set, the default used by Go is selected (see tls.Config.MaxVersion).
|
||||
MaxVersion uint16
|
||||
|
||||
selfCert bool
|
||||
|
||||
// parseFunc exists to simplify testing. Typically, parseFunc
|
||||
@ -205,7 +213,7 @@ func SelfCert(lg *zap.Logger, dirpath string, hosts []string, selfSignedCertVali
|
||||
)
|
||||
return
|
||||
}
|
||||
err = fileutil.TouchDirAll(dirpath)
|
||||
err = fileutil.TouchDirAll(lg, dirpath)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
@ -339,8 +347,8 @@ func SelfCert(lg *zap.Logger, dirpath string, hosts []string, selfSignedCertVali
|
||||
// Previously,
|
||||
// 1. Server has non-empty (*tls.Config).Certificates on client hello
|
||||
// 2. Server calls (*tls.Config).GetCertificate iff:
|
||||
// - Server's (*tls.Config).Certificates is not empty, or
|
||||
// - Client supplies SNI; non-empty (*tls.ClientHelloInfo).ServerName
|
||||
// - Server's (*tls.Config).Certificates is not empty, or
|
||||
// - Client supplies SNI; non-empty (*tls.ClientHelloInfo).ServerName
|
||||
//
|
||||
// When (*tls.Config).Certificates is always populated on initial handshake,
|
||||
// client is expected to provide a valid matching SNI to pass the TLS
|
||||
@ -378,8 +386,17 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var minVersion uint16
|
||||
if info.MinVersion != 0 {
|
||||
minVersion = info.MinVersion
|
||||
} else {
|
||||
// Default minimum version is TLS 1.2, previous versions are insecure and deprecated.
|
||||
minVersion = tls.VersionTLS12
|
||||
}
|
||||
|
||||
cfg := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
MinVersion: minVersion,
|
||||
MaxVersion: info.MaxVersion,
|
||||
ServerName: info.ServerName,
|
||||
}
|
||||
|
||||
@ -510,11 +527,6 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
||||
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
|
||||
cfg.NextProtos = []string{"h2"}
|
||||
|
||||
// go1.13 enables TLS 1.3 by default
|
||||
// and in TLS 1.3, cipher suites are not configurable
|
||||
// setting Max TLS version to TLS 1.2 for go 1.13
|
||||
cfg.MaxVersion = tls.VersionTLS12
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@ -569,11 +581,6 @@ func (info TLSInfo) ClientConfig() (*tls.Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// go1.13 enables TLS 1.3 by default
|
||||
// and in TLS 1.3, cipher suites are not configurable
|
||||
// setting Max TLS version to TLS 1.2 for go 1.13
|
||||
cfg.MaxVersion = tls.VersionTLS12
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
35
client/pkg/transport/sockopt_solaris.go
Normal file
35
client/pkg/transport/sockopt_solaris.go
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setReusePort(network, address string, c syscall.RawConn) error {
|
||||
return fmt.Errorf("port reuse is not supported on Solaris")
|
||||
}
|
||||
|
||||
func setReuseAddress(network, address string, conn syscall.RawConn) error {
|
||||
return conn.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1)
|
||||
})
|
||||
}
|
@ -1,5 +1,19 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
// 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.
|
||||
|
||||
//go:build !windows && !solaris
|
||||
// +build !windows,!solaris
|
||||
|
||||
package transport
|
||||
|
||||
|
@ -68,6 +68,5 @@ Use a custom context to set timeouts on your operations:
|
||||
// handle error
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
package client
|
||||
|
@ -1,12 +1,17 @@
|
||||
module go.etcd.io/etcd/client/v2
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/json-iterator/go v1.1.11
|
||||
github.com/modern-go/reflect2 v1.0.1
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
|
156
client/v2/go.sum
156
client/v2/go.sum
@ -1,172 +1,20 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
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/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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
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/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
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/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.27/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=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
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-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/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
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.2/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-20190108225652-1e06a53dbb7e/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/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-20200822124328-c89045814202/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20181221193216-37e7f081c4d4/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/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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=
|
||||
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-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
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=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
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=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
@ -61,7 +61,8 @@
|
||||
//
|
||||
// 1. context error: canceled or deadline exceeded.
|
||||
// 2. gRPC error: e.g. when clock drifts in server-side before client's context deadline exceeded.
|
||||
// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go
|
||||
//
|
||||
// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go
|
||||
//
|
||||
// Here is the example code to handle client errors:
|
||||
//
|
||||
@ -102,5 +103,4 @@
|
||||
// The grpc load balancer is registered statically and is shared across etcd clients.
|
||||
// To enable detailed load balancer logging, set the ETCD_CLIENT_DEBUG environment
|
||||
// variable. E.g. "ETCD_CLIENT_DEBUG=1".
|
||||
//
|
||||
package clientv3
|
||||
|
@ -45,25 +45,46 @@ func NewDoubleBarrier(s *concurrency.Session, key string, count int) *DoubleBarr
|
||||
// Enter waits for "count" processes to enter the barrier then returns
|
||||
func (b *DoubleBarrier) Enter() error {
|
||||
client := b.s.Client()
|
||||
|
||||
// Check the entered clients before creating the UniqueEphemeralKey,
|
||||
// fail the request if there are already too many clients.
|
||||
if resp1, err := b.enteredClients(client); err != nil {
|
||||
return err
|
||||
} else if len(resp1.Kvs) >= b.count {
|
||||
return ErrTooManyClients
|
||||
}
|
||||
|
||||
ek, err := newUniqueEphemeralKey(b.s, b.key+"/waiters")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.myKey = ek
|
||||
|
||||
resp, err := client.Get(b.ctx, b.key+"/waiters", clientv3.WithPrefix())
|
||||
// Check the entered clients after creating the UniqueEphemeralKey
|
||||
resp2, err := b.enteredClients(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp2.Kvs) >= b.count {
|
||||
lastWaiter := resp2.Kvs[b.count-1]
|
||||
if ek.rev > lastWaiter.CreateRevision {
|
||||
// delete itself now, otherwise other processes may need to wait
|
||||
// until these keys are automatically deleted when the related
|
||||
// lease expires.
|
||||
if err = b.myKey.Delete(); err != nil {
|
||||
// Nothing to do here. We have to wait for the key to be
|
||||
// deleted when the lease expires.
|
||||
}
|
||||
return ErrTooManyClients
|
||||
}
|
||||
|
||||
if len(resp.Kvs) > b.count {
|
||||
return ErrTooManyClients
|
||||
}
|
||||
|
||||
if len(resp.Kvs) == b.count {
|
||||
// unblock waiters
|
||||
_, err = client.Put(b.ctx, b.key+"/ready", "")
|
||||
return err
|
||||
if ek.rev == lastWaiter.CreateRevision {
|
||||
// TODO(ahrtr): we might need to compare ek.key and
|
||||
// string(lastWaiter.Key), they should be equal.
|
||||
// unblock all other waiters
|
||||
_, err = client.Put(b.ctx, b.key+"/ready", "")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = WaitEvents(
|
||||
@ -74,6 +95,18 @@ func (b *DoubleBarrier) Enter() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// enteredClients gets all the entered clients, which are ordered by the
|
||||
// createRevision in ascending order.
|
||||
func (b *DoubleBarrier) enteredClients(cli *clientv3.Client) (*clientv3.GetResponse, error) {
|
||||
resp, err := cli.Get(b.ctx, b.key+"/waiters", clientv3.WithPrefix(),
|
||||
clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Leave waits for "count" processes to leave the barrier then returns
|
||||
func (b *DoubleBarrier) Leave() error {
|
||||
client := b.s.Client()
|
||||
@ -96,7 +129,7 @@ func (b *DoubleBarrier) Leave() error {
|
||||
}
|
||||
isLowest := string(lowest.Key) == b.myKey.Key()
|
||||
|
||||
if len(resp.Kvs) == 1 {
|
||||
if len(resp.Kvs) == 1 && isLowest {
|
||||
// this is the only node in the barrier; finish up
|
||||
if _, err = client.Delete(b.ctx, b.key+"/ready"); err != nil {
|
||||
return err
|
||||
|
@ -1,18 +1,39 @@
|
||||
module go.etcd.io/etcd/client/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
go.uber.org/zap v1.17.0
|
||||
google.golang.org/grpc v1.41.0
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
go.etcd.io/etcd/api/v3 => ../../api
|
||||
go.etcd.io/etcd/client/pkg/v3 => ../pkg
|
||||
|
@ -135,8 +135,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/yuin/goldmark v1.1.27/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=
|
||||
@ -200,7 +200,6 @@ 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-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-20210403161142-5e06dd20ab57/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-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -268,8 +267,8 @@ 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
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/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
|
@ -45,8 +45,8 @@ func extractHostFromPath(pathStr string) string {
|
||||
return extractHostFromHostPort(path.Base(pathStr))
|
||||
}
|
||||
|
||||
//mustSplit2 returns the values from strings.SplitN(s, sep, 2).
|
||||
//If sep is not found, it returns ("", "", false) instead.
|
||||
// mustSplit2 returns the values from strings.SplitN(s, sep, 2).
|
||||
// If sep is not found, it returns ("", "", false) instead.
|
||||
func mustSplit2(s, sep string) (string, string) {
|
||||
spl := strings.SplitN(s, sep, 2)
|
||||
if len(spl) < 2 {
|
||||
@ -81,11 +81,12 @@ func schemeToCredsRequirement(schema string) CredsRequirement {
|
||||
// The main differences:
|
||||
// - etcd supports unixs & https names as opposed to unix & http to
|
||||
// distinguish need to configure certificates.
|
||||
// - etcd support http(s) names as opposed to tcp supported by grpc/dial method.
|
||||
// - etcd supports unix(s)://local-file naming schema
|
||||
// - etcd support http(s) names as opposed to tcp supported by grpc/dial method.
|
||||
// - etcd supports unix(s)://local-file naming schema
|
||||
// (as opposed to unix:local-file canonical name used by grpc for current dir files).
|
||||
// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon)
|
||||
// is considered serverName - to allow local testing of cert-protected communication.
|
||||
// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon)
|
||||
// is considered serverName - to allow local testing of cert-protected communication.
|
||||
//
|
||||
// See more:
|
||||
// - https://github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47
|
||||
// - https://golang.org/pkg/net/#Dial
|
||||
|
@ -19,28 +19,27 @@
|
||||
//
|
||||
// First, create a leasing KV from a clientv3.Client 'cli':
|
||||
//
|
||||
// lkv, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
//
|
||||
// A range request for a key "abc" tries to acquire a leasing key so it can cache the range's
|
||||
// key locally. On the server, the leasing key is stored to "leasing-prefix/abc":
|
||||
//
|
||||
// resp, err := lkv.Get(context.TODO(), "abc")
|
||||
// resp, err := lkv.Get(context.TODO(), "abc")
|
||||
//
|
||||
// Future linearized read requests using 'lkv' will be served locally for the lease's lifetime:
|
||||
//
|
||||
// resp, err = lkv.Get(context.TODO(), "abc")
|
||||
// resp, err = lkv.Get(context.TODO(), "abc")
|
||||
//
|
||||
// If another leasing client writes to a leased key, then the owner relinquishes its exclusive
|
||||
// access, permitting the writer to modify the key:
|
||||
//
|
||||
// lkv2, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv2.Put(context.TODO(), "abc", "456")
|
||||
// resp, err = lkv.Get("abc")
|
||||
//
|
||||
// lkv2, err := leasing.NewKV(cli, "leasing-prefix")
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// lkv2.Put(context.TODO(), "abc", "456")
|
||||
// resp, err = lkv.Get("abc")
|
||||
package leasing
|
||||
|
@ -92,6 +92,7 @@ func NewMaintenance(c *Client) Maintenance {
|
||||
err = c.getToken(dctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, nil, fmt.Errorf("failed to getToken from endpoint %s with maintenance client: %v", endpoint, err)
|
||||
}
|
||||
cancel = func() { conn.Close() }
|
||||
|
@ -39,5 +39,4 @@
|
||||
// resp, _ = cli.Get(context.TODO(), "abc")
|
||||
// fmt.Printf("%s\n", resp.Kvs[0].Value)
|
||||
// // Output: 456
|
||||
//
|
||||
package namespace
|
||||
|
@ -13,10 +13,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
// Package naming provides:
|
||||
// - subpackage endpoints: an abstraction layer to store and read endpoints
|
||||
// information from etcd.
|
||||
// - subpackage resolver: an etcd-backed gRPC resolver for discovering gRPC
|
||||
// services based on the endpoints configuration
|
||||
// - subpackage endpoints: an abstraction layer to store and read endpoints
|
||||
// information from etcd.
|
||||
// - subpackage resolver: an etcd-backed gRPC resolver for discovering gRPC
|
||||
// services based on the endpoints configuration
|
||||
//
|
||||
// To use, first import the packages:
|
||||
//
|
||||
@ -55,5 +55,4 @@
|
||||
// em := endpoints.NewManager(c, service)
|
||||
// return em.AddEndpoint(c.Ctx(), service+"/"+addr, endpoints.Endpoint{Addr:addr}, clientv3.WithLease(lid));
|
||||
// }
|
||||
//
|
||||
package naming
|
||||
|
@ -38,5 +38,4 @@
|
||||
// cli.KV = ordering.NewKV(cli.KV, vf)
|
||||
//
|
||||
// Now calls using 'cli' will reject order violations with an error.
|
||||
//
|
||||
package ordering
|
||||
|
@ -74,13 +74,7 @@ func (c *Client) unaryClientInterceptor(optFuncs ...retryOption) grpc.UnaryClien
|
||||
continue
|
||||
}
|
||||
if c.shouldRefreshToken(lastErr, callOpts) {
|
||||
// clear auth token before refreshing it.
|
||||
// call c.Auth.Authenticate with an invalid token will always fail the auth check on the server-side,
|
||||
// if the server has not apply the patch of pr #12165 (https://github.com/etcd-io/etcd/pull/12165)
|
||||
// and a rpctypes.ErrInvalidAuthToken will recursively call c.getToken until system run out of resource.
|
||||
c.authTokenBundle.UpdateAuthToken("")
|
||||
|
||||
gterr := c.getToken(ctx)
|
||||
gterr := c.refreshToken(ctx)
|
||||
if gterr != nil {
|
||||
c.GetLogger().Warn(
|
||||
"retrying of unary invoker failed to fetch new auth token",
|
||||
@ -161,6 +155,24 @@ func (c *Client) shouldRefreshToken(err error, callOpts *options) bool {
|
||||
(rpctypes.Error(err) == rpctypes.ErrInvalidAuthToken || rpctypes.Error(err) == rpctypes.ErrAuthOldRevision)
|
||||
}
|
||||
|
||||
func (c *Client) refreshToken(ctx context.Context) error {
|
||||
if c.authTokenBundle == nil {
|
||||
// c.authTokenBundle will be initialized only when
|
||||
// c.Username != "" && c.Password != "".
|
||||
//
|
||||
// When users use the TLS CommonName based authentication, the
|
||||
// authTokenBundle is always nil. But it's possible for the clients
|
||||
// to get `rpctypes.ErrAuthOldRevision` response when the clients
|
||||
// concurrently modify auth data (e.g, addUser, deleteUser etc.).
|
||||
// In this case, there is no need to refresh the token; instead the
|
||||
// clients just need to retry the operations (e.g. Put, Delete etc).
|
||||
return nil
|
||||
}
|
||||
// clear auth token before refreshing it.
|
||||
c.authTokenBundle.UpdateAuthToken("")
|
||||
return c.getToken(ctx)
|
||||
}
|
||||
|
||||
// type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a
|
||||
// proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish
|
||||
// a new ClientStream according to the retry policy.
|
||||
@ -259,10 +271,7 @@ func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{}
|
||||
return true, err
|
||||
}
|
||||
if s.client.shouldRefreshToken(err, s.callOpts) {
|
||||
// clear auth token to avoid failure when call getToken
|
||||
s.client.authTokenBundle.UpdateAuthToken("")
|
||||
|
||||
gterr := s.client.getToken(s.ctx)
|
||||
gterr := s.client.refreshToken(s.ctx)
|
||||
if gterr != nil {
|
||||
s.client.lg.Warn("retry failed to fetch new auth token", zap.Error(gterr))
|
||||
return false, err // return the original error for simplicity
|
||||
|
@ -25,15 +25,14 @@ import (
|
||||
|
||||
// Txn is the interface that wraps mini-transactions.
|
||||
//
|
||||
// Txn(context.TODO()).If(
|
||||
// Compare(Value(k1), ">", v1),
|
||||
// Compare(Version(k1), "=", 2)
|
||||
// ).Then(
|
||||
// OpPut(k2,v2), OpPut(k3,v3)
|
||||
// ).Else(
|
||||
// OpPut(k4,v4), OpPut(k5,v5)
|
||||
// ).Commit()
|
||||
//
|
||||
// Txn(context.TODO()).If(
|
||||
// Compare(Value(k1), ">", v1),
|
||||
// Compare(Version(k1), "=", 2)
|
||||
// ).Then(
|
||||
// OpPut(k2,v2), OpPut(k3,v3)
|
||||
// ).Else(
|
||||
// OpPut(k4,v4), OpPut(k5,v5)
|
||||
// ).Commit()
|
||||
type Txn interface {
|
||||
// If takes a list of comparison. If all comparisons passed in succeed,
|
||||
// the operations passed into Then() will be executed. Or the operations
|
||||
|
@ -37,6 +37,13 @@ const (
|
||||
EventTypePut = mvccpb.PUT
|
||||
|
||||
closeSendErrTimeout = 250 * time.Millisecond
|
||||
|
||||
// AutoWatchID is the watcher ID passed in WatchStream.Watch when no
|
||||
// user-provided ID is available. If pass, an ID will automatically be assigned.
|
||||
AutoWatchID = 0
|
||||
|
||||
// InvalidWatchID represents an invalid watch ID and prevents duplication with an existing watch.
|
||||
InvalidWatchID = -1
|
||||
)
|
||||
|
||||
type Event mvccpb.Event
|
||||
@ -450,7 +457,7 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) {
|
||||
|
||||
func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
|
||||
// check watch ID for backward compatibility (<= v3.3)
|
||||
if resp.WatchId == -1 || (resp.Canceled && resp.CancelReason != "") {
|
||||
if resp.WatchId == InvalidWatchID || (resp.Canceled && resp.CancelReason != "") {
|
||||
w.closeErr = v3rpc.Error(errors.New(resp.CancelReason))
|
||||
// failed; no channel
|
||||
close(ws.recvc)
|
||||
@ -481,7 +488,7 @@ func (w *watchGrpcStream) closeSubstream(ws *watcherStream) {
|
||||
} else if ws.outc != nil {
|
||||
close(ws.outc)
|
||||
}
|
||||
if ws.id != -1 {
|
||||
if ws.id != InvalidWatchID {
|
||||
delete(w.substreams, ws.id)
|
||||
return
|
||||
}
|
||||
@ -533,6 +540,7 @@ func (w *watchGrpcStream) run() {
|
||||
cancelSet := make(map[int64]struct{})
|
||||
|
||||
var cur *pb.WatchResponse
|
||||
backoff := time.Millisecond
|
||||
for {
|
||||
select {
|
||||
// Watch() requested
|
||||
@ -543,7 +551,7 @@ func (w *watchGrpcStream) run() {
|
||||
// TODO: pass custom watch ID?
|
||||
ws := &watcherStream{
|
||||
initReq: *wreq,
|
||||
id: -1,
|
||||
id: InvalidWatchID,
|
||||
outc: outc,
|
||||
// unbuffered so resumes won't cause repeat events
|
||||
recvc: make(chan *WatchResponse),
|
||||
@ -649,6 +657,7 @@ func (w *watchGrpcStream) run() {
|
||||
closeErr = err
|
||||
return
|
||||
}
|
||||
backoff = w.backoffIfUnavailable(backoff, err)
|
||||
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||
return
|
||||
}
|
||||
@ -669,7 +678,7 @@ func (w *watchGrpcStream) run() {
|
||||
if len(w.substreams)+len(w.resuming) == 0 {
|
||||
return
|
||||
}
|
||||
if ws.id != -1 {
|
||||
if ws.id != InvalidWatchID {
|
||||
// client is closing an established watch; close it on the server proactively instead of waiting
|
||||
// to close when the next message arrives
|
||||
cancelSet[ws.id] = struct{}{}
|
||||
@ -716,9 +725,9 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
|
||||
cancelReason: pbresp.CancelReason,
|
||||
}
|
||||
|
||||
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of -1 to
|
||||
// watch IDs are zero indexed, so request notify watch responses are assigned a watch ID of InvalidWatchID to
|
||||
// indicate they should be broadcast.
|
||||
if wr.IsProgressNotify() && pbresp.WatchId == -1 {
|
||||
if wr.IsProgressNotify() && pbresp.WatchId == InvalidWatchID {
|
||||
return w.broadcastResponse(wr)
|
||||
}
|
||||
|
||||
@ -839,7 +848,7 @@ func (w *watchGrpcStream) serveSubstream(ws *watcherStream, resumec chan struct{
|
||||
}
|
||||
} else {
|
||||
// current progress of watch; <= store revision
|
||||
nextRev = wr.Header.Revision
|
||||
nextRev = wr.Header.Revision + 1
|
||||
}
|
||||
|
||||
if len(wr.Events) > 0 {
|
||||
@ -873,7 +882,7 @@ func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
|
||||
w.resumec = make(chan struct{})
|
||||
w.joinSubstreams()
|
||||
for _, ws := range w.substreams {
|
||||
ws.id = -1
|
||||
ws.id = InvalidWatchID
|
||||
w.resuming = append(w.resuming, ws)
|
||||
}
|
||||
// strip out nils, if any
|
||||
@ -963,6 +972,21 @@ func (w *watchGrpcStream) joinSubstreams() {
|
||||
|
||||
var maxBackoff = 100 * time.Millisecond
|
||||
|
||||
func (w *watchGrpcStream) backoffIfUnavailable(backoff time.Duration, err error) time.Duration {
|
||||
if isUnavailableErr(w.ctx, err) {
|
||||
// retry, but backoff
|
||||
if backoff < maxBackoff {
|
||||
// 25% backoff factor
|
||||
backoff = backoff + backoff/4
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
time.Sleep(backoff)
|
||||
}
|
||||
return backoff
|
||||
}
|
||||
|
||||
// openWatchClient retries opening a watch client until success or halt.
|
||||
// manually retry in case "ws==nil && err==nil"
|
||||
// TODO: remove FailFast=false
|
||||
@ -983,17 +1007,7 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error)
|
||||
if isHaltErr(w.ctx, err) {
|
||||
return nil, v3rpc.Error(err)
|
||||
}
|
||||
if isUnavailableErr(w.ctx, err) {
|
||||
// retry, but backoff
|
||||
if backoff < maxBackoff {
|
||||
// 25% backoff factor
|
||||
backoff = backoff + backoff/4
|
||||
if backoff > maxBackoff {
|
||||
backoff = maxBackoff
|
||||
}
|
||||
}
|
||||
time.Sleep(backoff)
|
||||
}
|
||||
backoff = w.backoffIfUnavailable(backoff, err)
|
||||
}
|
||||
return ws, nil
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"go.etcd.io/etcd/client/v3"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/pkg/v3/cobrautl"
|
||||
)
|
||||
|
||||
@ -26,6 +27,7 @@ var (
|
||||
delPrefix bool
|
||||
delPrevKV bool
|
||||
delFromKey bool
|
||||
delRange bool
|
||||
)
|
||||
|
||||
// NewDelCommand returns the cobra command for "del".
|
||||
@ -39,6 +41,7 @@ func NewDelCommand() *cobra.Command {
|
||||
cmd.Flags().BoolVar(&delPrefix, "prefix", false, "delete keys with matching prefix")
|
||||
cmd.Flags().BoolVar(&delPrevKV, "prev-kv", false, "return deleted key-value pairs")
|
||||
cmd.Flags().BoolVar(&delFromKey, "from-key", false, "delete keys that are greater than or equal to the given key using byte compare")
|
||||
cmd.Flags().BoolVar(&delRange, "range", false, "delete range of keys")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -70,6 +73,9 @@ func getDelOp(args []string) (string, []clientv3.OpOption) {
|
||||
cobrautl.ExitWithError(cobrautl.ExitBadArgs, fmt.Errorf("too many arguments, only accept one argument when `--prefix` or `--from-key` is set"))
|
||||
}
|
||||
opts = append(opts, clientv3.WithRange(args[1]))
|
||||
if !delRange {
|
||||
fmt.Fprintln(os.Stderr, "In etcd v3.6, the operation will be suspended for a few seconds to provide the user time to verify range.")
|
||||
}
|
||||
}
|
||||
|
||||
if delPrefix {
|
||||
|
@ -1,6 +1,6 @@
|
||||
module go.etcd.io/etcd/etcdctl/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
@ -9,18 +9,58 @@ require (
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/urfave/cli v1.22.4
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/v2 v2.305.5
|
||||
go.etcd.io/etcd/client/v3 v3.5.5
|
||||
go.etcd.io/etcd/etcdutl/v3 v3.5.5
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/v2 v2.305.8
|
||||
go.etcd.io/etcd/client/v3 v3.5.8
|
||||
go.etcd.io/etcd/etcdutl/v3 v3.5.8
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.8
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
google.golang.org/grpc v1.41.0
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/prometheus/client_golang v1.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.8 // indirect
|
||||
go.etcd.io/etcd/server/v3 v3.5.8 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 // indirect
|
||||
go.opentelemetry.io/otel v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.0.1 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
go.etcd.io/etcd/api/v3 => ../api
|
||||
go.etcd.io/etcd/client/pkg/v3 => ../client/pkg
|
||||
|
@ -31,12 +31,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -45,17 +40,11 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
@ -63,8 +52,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@ -82,11 +69,7 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
@ -99,12 +82,12 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
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/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@ -121,7 +104,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -148,7 +130,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
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.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@ -188,7 +169,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -197,7 +177,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@ -227,7 +206,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -273,11 +251,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
@ -294,11 +270,10 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
@ -307,21 +282,17 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 h1:Wx7nFnvCaissIUZxPkBqDz2963Z+Cl+PkYbDKzTxDqQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E=
|
||||
go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc=
|
||||
go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk=
|
||||
go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI=
|
||||
go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw=
|
||||
go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@ -382,10 +353,9 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
@ -414,38 +384,32 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.1-0.20180807135948-17ff2d5776d2/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
|
||||
@ -491,7 +455,6 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||
@ -502,10 +465,8 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
|
||||
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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
|
||||
@ -531,7 +492,6 @@ 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/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -544,12 +504,11 @@ 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
@ -27,7 +27,7 @@ const (
|
||||
apiEnv = "ETCDCTL_API"
|
||||
)
|
||||
|
||||
/**
|
||||
/*
|
||||
mainWithError is fully analogous to main, but instead of signaling errors
|
||||
by os.Exit, it exposes the error explicitly, such that test-logic can intercept
|
||||
control to e.g. dump coverage data (even for test-for-failure scenarios).
|
||||
|
@ -36,7 +36,7 @@ func SplitTestArgs(args []string) (testArgs, appArgs []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Empty test to avoid no-tests warning.
|
||||
// TestEmpty to avoid no-tests warning.
|
||||
func TestEmpty(t *testing.T) {}
|
||||
|
||||
/**
|
||||
|
@ -114,7 +114,7 @@ func HandleBackup(withV3 bool, srcDir string, destDir string, srcWAL string, des
|
||||
destWAL = datadir.ToWalDir(destDir)
|
||||
}
|
||||
|
||||
if err := fileutil.CreateDirAll(destSnap); err != nil {
|
||||
if err := fileutil.CreateDirAll(lg, destSnap); err != nil {
|
||||
lg.Fatal("failed creating backup snapshot dir", zap.String("dest-snap", destSnap), zap.Error(err))
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
module go.etcd.io/etcd/etcdutl/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
replace (
|
||||
go.etcd.io/etcd/api/v3 => ../api
|
||||
@ -24,12 +24,50 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/spf13/cobra v1.1.3
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/v3 v3.5.5
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/raft/v3 v3.5.5
|
||||
go.etcd.io/etcd/server/v3 v3.5.5
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/v3 v3.5.8
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.8
|
||||
go.etcd.io/etcd/raft/v3 v3.5.8
|
||||
go.etcd.io/etcd/server/v3 v3.5.8
|
||||
go.uber.org/zap v1.17.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/prometheus/client_golang v1.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.8 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 // indirect
|
||||
go.opentelemetry.io/otel v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.0.1 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
)
|
||||
|
@ -30,12 +30,7 @@ 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.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -44,24 +39,16 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@ -78,11 +65,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
@ -95,12 +78,12 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
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/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
@ -117,7 +100,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -144,7 +126,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
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.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
@ -184,7 +165,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -193,7 +173,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@ -221,7 +200,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -265,11 +243,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
@ -286,32 +262,27 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 h1:Wx7nFnvCaissIUZxPkBqDz2963Z+Cl+PkYbDKzTxDqQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E=
|
||||
go.opentelemetry.io/otel v1.0.1 h1:4XKyXmfqJLOQ7feyV5DB6gsBFZ0ltB8vLtp6pj4JIcc=
|
||||
go.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk=
|
||||
go.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI=
|
||||
go.opentelemetry.io/otel/trace v1.0.1 h1:StTeIH6Q3G4r0Fiw34LTokUFESZgIDUr0qIJ7mKmAfw=
|
||||
go.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@ -372,10 +343,9 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/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-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
@ -404,38 +374,32 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.1-0.20180807135948-17ff2d5776d2/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
|
||||
@ -481,7 +445,6 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||
@ -492,10 +455,8 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
|
||||
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.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E=
|
||||
@ -519,7 +480,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -532,12 +492,11 @@ 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
@ -36,7 +36,7 @@ func SplitTestArgs(args []string) (testArgs, appArgs []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Empty test to avoid no-tests warning.
|
||||
// TestEmpty to avoid no-tests warning.
|
||||
func TestEmpty(t *testing.T) {}
|
||||
|
||||
/**
|
||||
|
@ -322,7 +322,7 @@ func (s *v3Manager) copyAndVerifyDB() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fileutil.CreateDirAll(s.snapDir); err != nil {
|
||||
if err := fileutil.CreateDirAll(s.lg, s.snapDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -383,7 +383,7 @@ func (s *v3Manager) copyAndVerifyDB() error {
|
||||
//
|
||||
// TODO: This code ignores learners !!!
|
||||
func (s *v3Manager) saveWALAndSnap() (*raftpb.HardState, error) {
|
||||
if err := fileutil.CreateDirAll(s.walDir); err != nil {
|
||||
if err := fileutil.CreateDirAll(s.lg, s.walDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
79
go.mod
79
go.mod
@ -1,6 +1,6 @@
|
||||
module go.etcd.io/etcd/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
replace (
|
||||
go.etcd.io/etcd/api/v3 => ./api
|
||||
@ -19,19 +19,74 @@ require (
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/spf13/cobra v1.1.3
|
||||
go.etcd.io/bbolt v1.3.6
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/v2 v2.305.5
|
||||
go.etcd.io/etcd/client/v3 v3.5.5
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.5
|
||||
go.etcd.io/etcd/etcdutl/v3 v3.5.5
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/raft/v3 v3.5.5
|
||||
go.etcd.io/etcd/server/v3 v3.5.5
|
||||
go.etcd.io/etcd/tests/v3 v3.5.5
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
go.etcd.io/etcd/api/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
go.etcd.io/etcd/client/v2 v2.305.8
|
||||
go.etcd.io/etcd/client/v3 v3.5.8
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.8
|
||||
go.etcd.io/etcd/etcdutl/v3 v3.5.8
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.8
|
||||
go.etcd.io/etcd/raft/v3 v3.5.8
|
||||
go.etcd.io/etcd/server/v3 v3.5.8
|
||||
go.etcd.io/etcd/tests/v3 v3.5.8
|
||||
go.uber.org/zap v1.17.0
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
|
||||
google.golang.org/grpc v1.41.0
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/btree v1.0.1 // 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-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/prometheus/client_golang v1.11.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/sirupsen/logrus v1.7.0 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 // indirect
|
||||
go.opentelemetry.io/otel v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.0.1 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.0.1 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.9.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
72
go.sum
72
go.sum
@ -35,10 +35,6 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
||||
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -47,25 +43,17 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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=
|
||||
@ -81,14 +69,9 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/etcd-io/gofail v0.0.0-20190801230047-ad7f989257ca/go.mod h1:49H/RkXP8pKaZy4h0d+NW16rSLhyVBt4o6VLJbmOqDE=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
@ -101,9 +84,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
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/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
@ -124,7 +108,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -194,23 +177,25 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
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/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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
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/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
@ -238,6 +223,7 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@ -272,6 +258,9 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
@ -302,21 +291,20 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
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/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0 h1:Wx7nFnvCaissIUZxPkBqDz2963Z+Cl+PkYbDKzTxDqQ=
|
||||
@ -396,8 +384,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
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.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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=
|
||||
@ -411,8 +399,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -432,33 +420,31 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.1-0.20180807135948-17ff2d5776d2/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
|
||||
@ -479,7 +465,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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=
|
||||
@ -507,8 +493,8 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 h1:R1r5J0u6Cx+RNl/6mezTw6oA14cmKC96FeUwL6A9bd4=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
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=
|
||||
@ -559,8 +545,8 @@ 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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -241,34 +241,34 @@ type intervalTree struct {
|
||||
//
|
||||
// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.4, p324
|
||||
//
|
||||
// 0. RB-DELETE(T, z)
|
||||
// 1.
|
||||
// 2. y = z
|
||||
// 3. y-original-color = y.color
|
||||
// 4.
|
||||
// 5. if z.left == T.nil
|
||||
// 6. x = z.right
|
||||
// 7. RB-TRANSPLANT(T, z, z.right)
|
||||
// 8. else if z.right == T.nil
|
||||
// 9. x = z.left
|
||||
// 10. RB-TRANSPLANT(T, z, z.left)
|
||||
// 11. else
|
||||
// 12. y = TREE-MINIMUM(z.right)
|
||||
// 13. y-original-color = y.color
|
||||
// 14. x = y.right
|
||||
// 15. if y.p == z
|
||||
// 16. x.p = y
|
||||
// 17. else
|
||||
// 18. RB-TRANSPLANT(T, y, y.right)
|
||||
// 19. y.right = z.right
|
||||
// 20. y.right.p = y
|
||||
// 21. RB-TRANSPLANT(T, z, y)
|
||||
// 22. y.left = z.left
|
||||
// 23. y.left.p = y
|
||||
// 24. y.color = z.color
|
||||
// 25.
|
||||
// 26. if y-original-color == BLACK
|
||||
// 27. RB-DELETE-FIXUP(T, x)
|
||||
// RB-DELETE(T, z)
|
||||
//
|
||||
// y = z
|
||||
// y-original-color = y.color
|
||||
//
|
||||
// if z.left == T.nil
|
||||
// x = z.right
|
||||
// RB-TRANSPLANT(T, z, z.right)
|
||||
// else if z.right == T.nil
|
||||
// x = z.left
|
||||
// RB-TRANSPLANT(T, z, z.left)
|
||||
// else
|
||||
// y = TREE-MINIMUM(z.right)
|
||||
// y-original-color = y.color
|
||||
// x = y.right
|
||||
// if y.p == z
|
||||
// x.p = y
|
||||
// else
|
||||
// RB-TRANSPLANT(T, y, y.right)
|
||||
// y.right = z.right
|
||||
// y.right.p = y
|
||||
// RB-TRANSPLANT(T, z, y)
|
||||
// y.left = z.left
|
||||
// y.left.p = y
|
||||
// y.color = z.color
|
||||
//
|
||||
// if y-original-color == BLACK
|
||||
// RB-DELETE-FIXUP(T, x)
|
||||
|
||||
// Delete removes the node with the given interval from the tree, returning
|
||||
// true if a node is in fact removed.
|
||||
@ -317,48 +317,47 @@ func (ivt *intervalTree) Delete(ivl Interval) bool {
|
||||
|
||||
// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.4, p326
|
||||
//
|
||||
// 0. RB-DELETE-FIXUP(T, z)
|
||||
// 1.
|
||||
// 2. while x ≠ T.root and x.color == BLACK
|
||||
// 3. if x == x.p.left
|
||||
// 4. w = x.p.right
|
||||
// 5. if w.color == RED
|
||||
// 6. w.color = BLACK
|
||||
// 7. x.p.color = RED
|
||||
// 8. LEFT-ROTATE(T, x, p)
|
||||
// 9. if w.left.color == BLACK and w.right.color == BLACK
|
||||
// 10. w.color = RED
|
||||
// 11. x = x.p
|
||||
// 12. else if w.right.color == BLACK
|
||||
// 13. w.left.color = BLACK
|
||||
// 14. w.color = RED
|
||||
// 15. RIGHT-ROTATE(T, w)
|
||||
// 16. w = w.p.right
|
||||
// 17. w.color = x.p.color
|
||||
// 18. x.p.color = BLACK
|
||||
// 19. LEFT-ROTATE(T, w.p)
|
||||
// 20. x = T.root
|
||||
// 21. else
|
||||
// 22. w = x.p.left
|
||||
// 23. if w.color == RED
|
||||
// 24. w.color = BLACK
|
||||
// 25. x.p.color = RED
|
||||
// 26. RIGHT-ROTATE(T, x, p)
|
||||
// 27. if w.right.color == BLACK and w.left.color == BLACK
|
||||
// 28. w.color = RED
|
||||
// 29. x = x.p
|
||||
// 30. else if w.left.color == BLACK
|
||||
// 31. w.right.color = BLACK
|
||||
// 32. w.color = RED
|
||||
// 33. LEFT-ROTATE(T, w)
|
||||
// 34. w = w.p.left
|
||||
// 35. w.color = x.p.color
|
||||
// 36. x.p.color = BLACK
|
||||
// 37. RIGHT-ROTATE(T, w.p)
|
||||
// 38. x = T.root
|
||||
// 39.
|
||||
// 40. x.color = BLACK
|
||||
// RB-DELETE-FIXUP(T, z)
|
||||
//
|
||||
// while x ≠ T.root and x.color == BLACK
|
||||
// if x == x.p.left
|
||||
// w = x.p.right
|
||||
// if w.color == RED
|
||||
// w.color = BLACK
|
||||
// x.p.color = RED
|
||||
// LEFT-ROTATE(T, x, p)
|
||||
// if w.left.color == BLACK and w.right.color == BLACK
|
||||
// w.color = RED
|
||||
// x = x.p
|
||||
// else if w.right.color == BLACK
|
||||
// w.left.color = BLACK
|
||||
// w.color = RED
|
||||
// RIGHT-ROTATE(T, w)
|
||||
// w = w.p.right
|
||||
// w.color = x.p.color
|
||||
// x.p.color = BLACK
|
||||
// LEFT-ROTATE(T, w.p)
|
||||
// x = T.root
|
||||
// else
|
||||
// w = x.p.left
|
||||
// if w.color == RED
|
||||
// w.color = BLACK
|
||||
// x.p.color = RED
|
||||
// RIGHT-ROTATE(T, x, p)
|
||||
// if w.right.color == BLACK and w.left.color == BLACK
|
||||
// w.color = RED
|
||||
// x = x.p
|
||||
// else if w.left.color == BLACK
|
||||
// w.right.color = BLACK
|
||||
// w.color = RED
|
||||
// LEFT-ROTATE(T, w)
|
||||
// w = w.p.left
|
||||
// w.color = x.p.color
|
||||
// x.p.color = BLACK
|
||||
// RIGHT-ROTATE(T, w.p)
|
||||
// x = T.root
|
||||
//
|
||||
// x.color = BLACK
|
||||
func (ivt *intervalTree) deleteFixup(x *intervalNode) {
|
||||
for x != ivt.root && x.color(ivt.sentinel) == black {
|
||||
if x == x.parent.left { // line 3-20
|
||||
@ -439,32 +438,32 @@ func (ivt *intervalTree) createIntervalNode(ivl Interval, val interface{}) *inte
|
||||
//
|
||||
// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.3, p315
|
||||
//
|
||||
// 0. RB-INSERT(T, z)
|
||||
// 1.
|
||||
// 2. y = T.nil
|
||||
// 3. x = T.root
|
||||
// 4.
|
||||
// 5. while x ≠ T.nil
|
||||
// 6. y = x
|
||||
// 7. if z.key < x.key
|
||||
// 8. x = x.left
|
||||
// 9. else
|
||||
// 10. x = x.right
|
||||
// 11.
|
||||
// 12. z.p = y
|
||||
// 13.
|
||||
// 14. if y == T.nil
|
||||
// 15. T.root = z
|
||||
// 16. else if z.key < y.key
|
||||
// 17. y.left = z
|
||||
// 18. else
|
||||
// 19. y.right = z
|
||||
// 20.
|
||||
// 21. z.left = T.nil
|
||||
// 22. z.right = T.nil
|
||||
// 23. z.color = RED
|
||||
// 24.
|
||||
// 25. RB-INSERT-FIXUP(T, z)
|
||||
// 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.
|
||||
func (ivt *intervalTree) Insert(ivl Interval, val interface{}) {
|
||||
@ -499,38 +498,37 @@ func (ivt *intervalTree) Insert(ivl Interval, val interface{}) {
|
||||
|
||||
// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.3, p316
|
||||
//
|
||||
// 0. RB-INSERT-FIXUP(T, z)
|
||||
// 1.
|
||||
// 2. while z.p.color == RED
|
||||
// 3. if z.p == z.p.p.left
|
||||
// 4. y = z.p.p.right
|
||||
// 5. if y.color == RED
|
||||
// 6. z.p.color = BLACK
|
||||
// 7. y.color = BLACK
|
||||
// 8. z.p.p.color = RED
|
||||
// 9. z = z.p.p
|
||||
// 10. else if z == z.p.right
|
||||
// 11. z = z.p
|
||||
// 12. LEFT-ROTATE(T, z)
|
||||
// 13. z.p.color = BLACK
|
||||
// 14. z.p.p.color = RED
|
||||
// 15. RIGHT-ROTATE(T, z.p.p)
|
||||
// 16. else
|
||||
// 17. y = z.p.p.left
|
||||
// 18. if y.color == RED
|
||||
// 19. z.p.color = BLACK
|
||||
// 20. y.color = BLACK
|
||||
// 21. z.p.p.color = RED
|
||||
// 22. z = z.p.p
|
||||
// 23. else if z == z.p.right
|
||||
// 24. z = z.p
|
||||
// 25. RIGHT-ROTATE(T, z)
|
||||
// 26. z.p.color = BLACK
|
||||
// 27. z.p.p.color = RED
|
||||
// 28. LEFT-ROTATE(T, z.p.p)
|
||||
// 29.
|
||||
// 30. T.root.color = BLACK
|
||||
// RB-INSERT-FIXUP(T, z)
|
||||
//
|
||||
// while z.p.color == RED
|
||||
// if z.p == z.p.p.left
|
||||
// y = z.p.p.right
|
||||
// if y.color == RED
|
||||
// z.p.color = BLACK
|
||||
// y.color = BLACK
|
||||
// z.p.p.color = RED
|
||||
// z = z.p.p
|
||||
// else if z == z.p.right
|
||||
// z = z.p
|
||||
// LEFT-ROTATE(T, z)
|
||||
// z.p.color = BLACK
|
||||
// z.p.p.color = RED
|
||||
// RIGHT-ROTATE(T, z.p.p)
|
||||
// else
|
||||
// y = z.p.p.left
|
||||
// if y.color == RED
|
||||
// z.p.color = BLACK
|
||||
// y.color = BLACK
|
||||
// z.p.p.color = RED
|
||||
// z = z.p.p
|
||||
// else if z == z.p.right
|
||||
// z = z.p
|
||||
// RIGHT-ROTATE(T, z)
|
||||
// z.p.color = BLACK
|
||||
// z.p.p.color = RED
|
||||
// LEFT-ROTATE(T, z.p.p)
|
||||
//
|
||||
// T.root.color = BLACK
|
||||
func (ivt *intervalTree) insertFixup(z *intervalNode) {
|
||||
for z.parent.color(ivt.sentinel) == red {
|
||||
if z.parent == z.parent.parent.left { // line 3-15
|
||||
@ -578,26 +576,25 @@ func (ivt *intervalTree) insertFixup(z *intervalNode) {
|
||||
//
|
||||
// "Introduction to Algorithms" (Cormen et al, 3rd ed.), chapter 13.2, p313
|
||||
//
|
||||
// 0. LEFT-ROTATE(T, x)
|
||||
// 1.
|
||||
// 2. y = x.right
|
||||
// 3. x.right = y.left
|
||||
// 4.
|
||||
// 5. if y.left ≠ T.nil
|
||||
// 6. y.left.p = x
|
||||
// 7.
|
||||
// 8. y.p = x.p
|
||||
// 9.
|
||||
// 10. if x.p == T.nil
|
||||
// 11. T.root = y
|
||||
// 12. else if x == x.p.left
|
||||
// 13. x.p.left = y
|
||||
// 14. else
|
||||
// 15. x.p.right = y
|
||||
// 16.
|
||||
// 17. y.left = x
|
||||
// 18. x.p = y
|
||||
// LEFT-ROTATE(T, x)
|
||||
//
|
||||
// y = x.right
|
||||
// x.right = y.left
|
||||
//
|
||||
// if y.left ≠ T.nil
|
||||
// y.left.p = x
|
||||
//
|
||||
// y.p = x.p
|
||||
//
|
||||
// if x.p == T.nil
|
||||
// T.root = y
|
||||
// else if x == x.p.left
|
||||
// x.p.left = y
|
||||
// else
|
||||
// x.p.right = y
|
||||
//
|
||||
// y.left = x
|
||||
// x.p = y
|
||||
func (ivt *intervalTree) rotateLeft(x *intervalNode) {
|
||||
// rotateLeft x must have right child
|
||||
if x.right == ivt.sentinel {
|
||||
@ -624,26 +621,25 @@ func (ivt *intervalTree) rotateLeft(x *intervalNode) {
|
||||
|
||||
// rotateRight moves x so it is right of its left child
|
||||
//
|
||||
// 0. RIGHT-ROTATE(T, x)
|
||||
// 1.
|
||||
// 2. y = x.left
|
||||
// 3. x.left = y.right
|
||||
// 4.
|
||||
// 5. if y.right ≠ T.nil
|
||||
// 6. y.right.p = x
|
||||
// 7.
|
||||
// 8. y.p = x.p
|
||||
// 9.
|
||||
// 10. if x.p == T.nil
|
||||
// 11. T.root = y
|
||||
// 12. else if x == x.p.right
|
||||
// 13. x.p.right = y
|
||||
// 14. else
|
||||
// 15. x.p.left = y
|
||||
// 16.
|
||||
// 17. y.right = x
|
||||
// 18. x.p = y
|
||||
// RIGHT-ROTATE(T, x)
|
||||
//
|
||||
// y = x.left
|
||||
// x.left = y.right
|
||||
//
|
||||
// if y.right ≠ T.nil
|
||||
// y.right.p = x
|
||||
//
|
||||
// y.p = x.p
|
||||
//
|
||||
// if x.p == T.nil
|
||||
// T.root = y
|
||||
// else if x == x.p.right
|
||||
// x.p.right = y
|
||||
// else
|
||||
// x.p.left = y
|
||||
//
|
||||
// y.right = x
|
||||
// x.p = y
|
||||
func (ivt *intervalTree) rotateRight(x *intervalNode) {
|
||||
// rotateRight x must have left child
|
||||
if x.left == ivt.sentinel {
|
||||
|
@ -63,27 +63,28 @@ func TestIntervalTreeInsert(t *testing.T) {
|
||||
// Use https://www.cs.usfca.edu/~galles/visualization/RedBlack.html for test case creation.
|
||||
//
|
||||
// Regular Binary Search Tree
|
||||
// [0,1]
|
||||
// \
|
||||
// [1,2]
|
||||
// \
|
||||
// [3,4]
|
||||
// \
|
||||
// [5,6]
|
||||
// \
|
||||
// [7,8]
|
||||
// \
|
||||
// [8,9]
|
||||
//
|
||||
// [0,1]
|
||||
// \
|
||||
// [1,2]
|
||||
// \
|
||||
// [3,4]
|
||||
// \
|
||||
// [5,6]
|
||||
// \
|
||||
// [7,8]
|
||||
// \
|
||||
// [8,9]
|
||||
//
|
||||
// Self-Balancing Binary Search Tree
|
||||
// [1,2]
|
||||
// / \
|
||||
// [0,1] [5,6]
|
||||
// / \
|
||||
// [3,4] [7,8]
|
||||
// \
|
||||
// [8,9]
|
||||
//
|
||||
// [1,2]
|
||||
// / \
|
||||
// [0,1] [5,6]
|
||||
// / \
|
||||
// [3,4] [7,8]
|
||||
// \
|
||||
// [8,9]
|
||||
func TestIntervalTreeSelfBalanced(t *testing.T) {
|
||||
ivt := NewIntervalTree()
|
||||
ivt.Insert(NewInt64Interval(0, 1), 0)
|
||||
@ -120,58 +121,56 @@ func TestIntervalTreeSelfBalanced(t *testing.T) {
|
||||
// Use https://www.cs.usfca.edu/~galles/visualization/RedBlack.html for test case creation.
|
||||
// See https://github.com/etcd-io/etcd/issues/10877 for more detail.
|
||||
//
|
||||
//
|
||||
// After insertion:
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- -----------------------
|
||||
// / \
|
||||
// [82,83] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [11,12] [383,384](red) [647,648] [899,900](red)
|
||||
// / \ / \ / \
|
||||
// / \ / \ / \
|
||||
// [261,262] [410,411] [514,515](red) [815,816](red) [888,889] [972,973]
|
||||
// / \ /
|
||||
// / \ /
|
||||
// [238,239](red) [292,293](red) [953,954](red)
|
||||
//
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- -----------------------
|
||||
// / \
|
||||
// [82,83] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [11,12] [383,384](red) [647,648] [899,900](red)
|
||||
// / \ / \ / \
|
||||
// / \ / \ / \
|
||||
// [261,262] [410,411] [514,515](red) [815,816](red) [888,889] [972,973]
|
||||
// / \ /
|
||||
// / \ /
|
||||
// [238,239](red) [292,293](red) [953,954](red)
|
||||
//
|
||||
// After deleting 514 (no rebalance):
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- -----------------------
|
||||
// / \
|
||||
// [82,83] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [11,12] [383,384](red) [647,648] [899,900](red)
|
||||
// / \ \ / \
|
||||
// / \ \ / \
|
||||
// [261,262] [410,411] [815,816](red) [888,889] [972,973]
|
||||
// / \ /
|
||||
// / \ /
|
||||
// [238,239](red) [292,293](red) [953,954](red)
|
||||
//
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- -----------------------
|
||||
// / \
|
||||
// [82,83] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [11,12] [383,384](red) [647,648] [899,900](red)
|
||||
// / \ \ / \
|
||||
// / \ \ / \
|
||||
// [261,262] [410,411] [815,816](red) [888,889] [972,973]
|
||||
// / \ /
|
||||
// / \ /
|
||||
// [238,239](red) [292,293](red) [953,954](red)
|
||||
//
|
||||
// After deleting 11 (requires rebalancing):
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- --------------------------
|
||||
// / \
|
||||
// [383,384] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [261,262](red) [410,411] [647,648] [899,900](red)
|
||||
// / \ \ / \
|
||||
// / \ \ / \
|
||||
// [82,83] [292,293] [815,816](red) [888,889] [972,973]
|
||||
// \ /
|
||||
// \ /
|
||||
// [238,239](red) [953,954](red)
|
||||
//
|
||||
//
|
||||
// [510,511]
|
||||
// / \
|
||||
// ---------- --------------------------
|
||||
// / \
|
||||
// [383,384] [830,831]
|
||||
// / \ / \
|
||||
// / \ / \
|
||||
// [261,262](red) [410,411] [647,648] [899,900](red)
|
||||
// / \ \ / \
|
||||
// / \ \ / \
|
||||
// [82,83] [292,293] [815,816](red) [888,889] [972,973]
|
||||
// \ /
|
||||
// \ /
|
||||
// [238,239](red) [953,954](red)
|
||||
func TestIntervalTreeDelete(t *testing.T) {
|
||||
ivt := NewIntervalTree()
|
||||
ivt.Insert(NewInt64Interval(510, 511), 0)
|
||||
|
@ -149,6 +149,11 @@ func (ep *ExpectProcess) Signal(sig os.Signal) error {
|
||||
return ep.cmd.Process.Signal(sig)
|
||||
}
|
||||
|
||||
// Wait waits for the process to finish.
|
||||
func (ep *ExpectProcess) Wait() {
|
||||
ep.wg.Wait()
|
||||
}
|
||||
|
||||
// Close waits for the expect process to exit.
|
||||
// Close currently does not return error if process exited with !=0 status.
|
||||
// TODO: Close should expose underlying proces failure by default.
|
||||
|
22
pkg/go.mod
22
pkg/go.mod
@ -1,19 +1,33 @@
|
||||
module go.etcd.io/etcd/pkg/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/golang/protobuf v1.5.1 // indirect
|
||||
github.com/spf13/cobra v1.1.3
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
go.uber.org/zap v1.17.0
|
||||
google.golang.org/grpc v1.41.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.5.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
go.etcd.io/etcd => ./FORBIDDEN_DEPENDENCY
|
||||
go.etcd.io/etcd/api/v3 => ./FORBIDDEN_DEPENDENCY
|
||||
|
12
pkg/go.sum
12
pkg/go.sum
@ -35,7 +35,6 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
@ -61,7 +60,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
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.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@ -200,11 +198,16 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
@ -373,8 +376,9 @@ gopkg.in/yaml.v2 v2.2.8/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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -44,7 +44,7 @@ func TestPageWriterRandom(t *testing.T) {
|
||||
t.Logf("total write bytes: %d (of %d)", cw.writeBytes, n)
|
||||
}
|
||||
|
||||
// TestPageWriterPariallack tests the case where a write overflows the buffer
|
||||
// TestPageWriterPartialSlack tests the case where a write overflows the buffer
|
||||
// but there is not enough data to complete the slack write.
|
||||
func TestPageWriterPartialSlack(t *testing.T) {
|
||||
defaultBufferBytes = 1024
|
||||
|
@ -103,7 +103,7 @@ func resolveURL(ctx context.Context, lg *zap.Logger, u url.URL) (string, error)
|
||||
)
|
||||
return "", err
|
||||
}
|
||||
if host == "localhost" || net.ParseIP(host) != nil {
|
||||
if host == "localhost" {
|
||||
return "", nil
|
||||
}
|
||||
for ctx.Err() == nil {
|
||||
@ -148,20 +148,31 @@ func urlsEqual(ctx context.Context, lg *zap.Logger, a []url.URL, b []url.URL) (b
|
||||
if len(a) != len(b) {
|
||||
return false, fmt.Errorf("len(%q) != len(%q)", urlsToStrings(a), urlsToStrings(b))
|
||||
}
|
||||
|
||||
sort.Sort(types.URLs(a))
|
||||
sort.Sort(types.URLs(b))
|
||||
var needResolve bool
|
||||
for i := range a {
|
||||
if !reflect.DeepEqual(a[i], b[i]) {
|
||||
needResolve = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !needResolve {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If URLs are not equal, try to resolve it and compare again.
|
||||
urls, err := resolveTCPAddrs(ctx, lg, [][]url.URL{a, b})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
preva, prevb := a, b
|
||||
a, b = urls[0], urls[1]
|
||||
sort.Sort(types.URLs(a))
|
||||
sort.Sort(types.URLs(b))
|
||||
for i := range a {
|
||||
if !reflect.DeepEqual(a[i], b[i]) {
|
||||
return false, fmt.Errorf("%q(resolved from %q) != %q(resolved from %q)",
|
||||
a[i].String(), preva[i].String(),
|
||||
b[i].String(), prevb[i].String(),
|
||||
)
|
||||
return false, fmt.Errorf("resolved urls: %q != %q", a[i].String(), b[i].String())
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
@ -174,21 +185,13 @@ func URLStringsEqual(ctx context.Context, lg *zap.Logger, a []string, b []string
|
||||
if len(a) != len(b) {
|
||||
return false, fmt.Errorf("len(%q) != len(%q)", a, b)
|
||||
}
|
||||
urlsA := make([]url.URL, 0)
|
||||
for _, str := range a {
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse %q", str)
|
||||
}
|
||||
urlsA = append(urlsA, *u)
|
||||
urlsA, err := stringsToURLs(a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
urlsB := make([]url.URL, 0)
|
||||
for _, str := range b {
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to parse %q", str)
|
||||
}
|
||||
urlsB = append(urlsB, *u)
|
||||
urlsB, err := stringsToURLs(b)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return urlsEqual(ctx, lg, urlsA, urlsB)
|
||||
}
|
||||
@ -201,6 +204,18 @@ func urlsToStrings(us []url.URL) []string {
|
||||
return rs
|
||||
}
|
||||
|
||||
func stringsToURLs(us []string) ([]url.URL, error) {
|
||||
urls := make([]url.URL, 0, len(us))
|
||||
for _, str := range us {
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse string to URL: %q", str)
|
||||
}
|
||||
urls = append(urls, *u)
|
||||
}
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
func IsNetworkTimeoutError(err error) bool {
|
||||
nerr, ok := err.(net.Error)
|
||||
return ok && nerr.Timeout()
|
||||
|
@ -17,6 +17,7 @@ package netutil
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@ -119,13 +120,16 @@ func TestResolveTCPAddrs(t *testing.T) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tt.hostMap[host] == "" {
|
||||
return nil, errors.New("cannot resolve host")
|
||||
}
|
||||
i, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return &net.TCPAddr{IP: ip, Port: i, Zone: ""}, nil
|
||||
}
|
||||
if tt.hostMap[host] == "" {
|
||||
return nil, errors.New("cannot resolve host")
|
||||
}
|
||||
return &net.TCPAddr{IP: net.ParseIP(tt.hostMap[host]), Port: i, Zone: ""}, nil
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
|
||||
@ -151,128 +155,151 @@ func TestURLsEqual(t *testing.T) {
|
||||
"second.com": "10.0.11.2",
|
||||
}
|
||||
resolveTCPAddr = func(ctx context.Context, addr string) (*net.TCPAddr, error) {
|
||||
host, port, herr := net.SplitHostPort(addr)
|
||||
if herr != nil {
|
||||
return nil, herr
|
||||
}
|
||||
if _, ok := hostm[host]; !ok {
|
||||
return nil, errors.New("cannot resolve host.")
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return &net.TCPAddr{IP: ip, Port: i, Zone: ""}, nil
|
||||
}
|
||||
if hostm[host] == "" {
|
||||
return nil, errors.New("cannot resolve host")
|
||||
}
|
||||
return &net.TCPAddr{IP: net.ParseIP(hostm[host]), Port: i, Zone: ""}, nil
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
n int
|
||||
a []url.URL
|
||||
b []url.URL
|
||||
expect bool
|
||||
err error
|
||||
}{
|
||||
{
|
||||
n: 0,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 1,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 2,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}},
|
||||
b: []url.URL{{Scheme: "https", Host: "10.0.10.1:2379"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://10.0.10.1:2379"(resolved from "http://example.com:2379") != "https://10.0.10.1:2379"(resolved from "https://10.0.10.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://10.0.10.1:2379" != "https://10.0.10.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 3,
|
||||
a: []url.URL{{Scheme: "https", Host: "example.com:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}},
|
||||
expect: false,
|
||||
err: errors.New(`"https://10.0.10.1:2379"(resolved from "https://example.com:2379") != "http://10.0.10.1:2379"(resolved from "http://10.0.10.1:2379")`),
|
||||
err: errors.New(`resolved urls: "https://10.0.10.1:2379" != "http://10.0.10.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 4,
|
||||
a: []url.URL{{Scheme: "unix", Host: "abc:2379"}},
|
||||
b: []url.URL{{Scheme: "unix", Host: "abc:2379"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 5,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 6,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 7,
|
||||
a: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 8,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://127.0.0.1:2379"(resolved from "http://127.0.0.1:2379") != "http://127.0.0.1:2380"(resolved from "http://127.0.0.1:2380")`),
|
||||
err: errors.New(`resolved urls: "http://127.0.0.1:2379" != "http://127.0.0.1:2380"`),
|
||||
},
|
||||
{
|
||||
n: 9,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://10.0.10.1:2380"(resolved from "http://example.com:2380") != "http://10.0.10.1:2379"(resolved from "http://10.0.10.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://10.0.10.1:2380" != "http://10.0.10.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 10,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://127.0.0.1:2379"(resolved from "http://127.0.0.1:2379") != "http://10.0.0.1:2379"(resolved from "http://10.0.0.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://127.0.0.1:2379" != "http://10.0.0.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 11,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://10.0.10.1:2379"(resolved from "http://example.com:2379") != "http://10.0.0.1:2379"(resolved from "http://10.0.0.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://10.0.10.1:2379" != "http://10.0.0.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 12,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://127.0.0.1:2379"(resolved from "http://127.0.0.1:2379") != "http://127.0.0.1:2380"(resolved from "http://127.0.0.1:2380")`),
|
||||
err: errors.New(`resolved urls: "http://127.0.0.1:2379" != "http://127.0.0.1:2380"`),
|
||||
},
|
||||
{
|
||||
n: 13,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://10.0.10.1:2379"(resolved from "http://example.com:2379") != "http://127.0.0.1:2380"(resolved from "http://127.0.0.1:2380")`),
|
||||
err: errors.New(`resolved urls: "http://10.0.10.1:2379" != "http://127.0.0.1:2380"`),
|
||||
},
|
||||
{
|
||||
n: 14,
|
||||
a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://127.0.0.1:2379"(resolved from "http://127.0.0.1:2379") != "http://10.0.0.1:2379"(resolved from "http://10.0.0.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://127.0.0.1:2379" != "http://10.0.0.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 15,
|
||||
a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`"http://10.0.10.1:2379"(resolved from "http://example.com:2379") != "http://10.0.0.1:2379"(resolved from "http://10.0.0.1:2379")`),
|
||||
err: errors.New(`resolved urls: "http://10.0.10.1:2379" != "http://10.0.0.1:2379"`),
|
||||
},
|
||||
{
|
||||
n: 16,
|
||||
a: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
|
||||
expect: false,
|
||||
err: errors.New(`len(["http://10.0.0.1:2379"]) != len(["http://10.0.0.1:2379" "http://127.0.0.1:2380"])`),
|
||||
},
|
||||
{
|
||||
n: 17,
|
||||
a: []url.URL{{Scheme: "http", Host: "first.com:2379"}, {Scheme: "http", Host: "second.com:2380"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.11.1:2379"}, {Scheme: "http", Host: "10.0.11.2:2380"}},
|
||||
expect: true,
|
||||
},
|
||||
{
|
||||
n: 18,
|
||||
a: []url.URL{{Scheme: "http", Host: "second.com:2380"}, {Scheme: "http", Host: "first.com:2379"}},
|
||||
b: []url.URL{{Scheme: "http", Host: "10.0.11.1:2379"}, {Scheme: "http", Host: "10.0.11.2:2380"}},
|
||||
expect: true,
|
||||
@ -282,21 +309,48 @@ func TestURLsEqual(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
result, err := urlsEqual(context.TODO(), zap.NewExample(), test.a, test.b)
|
||||
if result != test.expect {
|
||||
t.Errorf("#%d: a:%v b:%v, expected %v but %v", i, test.a, test.b, test.expect, result)
|
||||
t.Errorf("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() {
|
||||
t.Errorf("#%d: err expected %v but %v", i, test.err, err)
|
||||
t.Errorf("idx=%d #%d: err expected %v but %v", i, test.n, test.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestURLStringsEqual(t *testing.T) {
|
||||
result, err := URLStringsEqual(context.TODO(), zap.NewExample(), []string{"http://127.0.0.1:8080"}, []string{"http://127.0.0.1:8080"})
|
||||
if !result {
|
||||
t.Errorf("unexpected result %v", result)
|
||||
defer func() { resolveTCPAddr = resolveTCPAddrDefault }()
|
||||
errOnResolve := func(ctx context.Context, addr string) (*net.TCPAddr, error) {
|
||||
return nil, fmt.Errorf("unexpected attempt to resolve: %q", addr)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
cases := []struct {
|
||||
urlsA []string
|
||||
urlsB []string
|
||||
resolver func(ctx context.Context, addr string) (*net.TCPAddr, error)
|
||||
}{
|
||||
{[]string{"http://127.0.0.1:8080"}, []string{"http://127.0.0.1:8080"}, resolveTCPAddrDefault},
|
||||
{[]string{
|
||||
"http://host1:8080",
|
||||
"http://host2:8080",
|
||||
}, []string{
|
||||
"http://host1:8080",
|
||||
"http://host2:8080",
|
||||
}, errOnResolve},
|
||||
{
|
||||
urlsA: []string{"https://[c262:266f:fa53:0ee6:966e:e3f0:d68f:b046]:2380"},
|
||||
urlsB: []string{"https://[c262:266f:fa53:ee6:966e:e3f0:d68f:b046]:2380"},
|
||||
resolver: resolveTCPAddrDefault,
|
||||
},
|
||||
}
|
||||
for idx, c := range cases {
|
||||
t.Logf("TestURLStringsEqual, case #%d", idx)
|
||||
resolveTCPAddr = c.resolver
|
||||
result, err := URLStringsEqual(context.TODO(), zap.NewExample(), c.urlsA, c.urlsB)
|
||||
if !result {
|
||||
t.Errorf("unexpected result %v", result)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ func UniqueStrings(slen uint, n int) (ss []string) {
|
||||
exist := make(map[string]struct{})
|
||||
ss = make([]string, 0, n)
|
||||
for len(ss) < n {
|
||||
s := randString(slen)
|
||||
s := RandString(slen)
|
||||
if _, ok := exist[s]; !ok {
|
||||
ss = append(ss, s)
|
||||
exist[s] = struct{}{}
|
||||
@ -37,14 +37,14 @@ func UniqueStrings(slen uint, n int) (ss []string) {
|
||||
func RandomStrings(slen uint, n int) (ss []string) {
|
||||
ss = make([]string, 0, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ss = append(ss, randString(slen))
|
||||
ss = append(ss, RandString(slen))
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func randString(l uint) string {
|
||||
func RandString(l uint) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
s := make([]byte, l)
|
||||
for i := 0; i < int(l); i++ {
|
||||
|
@ -37,9 +37,11 @@ type Changer struct {
|
||||
// config is empty and initializes it with a copy of the incoming (=left)
|
||||
// majority config. That is, it transitions from
|
||||
//
|
||||
// (1 2 3)&&()
|
||||
// (1 2 3)&&()
|
||||
//
|
||||
// to
|
||||
// (1 2 3)&&(1 2 3).
|
||||
//
|
||||
// (1 2 3)&&(1 2 3).
|
||||
//
|
||||
// The supplied changes are then applied to the incoming majority config,
|
||||
// resulting in a joint configuration that in terms of the Raft thesis[1]
|
||||
|
107
raft/doc.go
107
raft/doc.go
@ -25,46 +25,46 @@ A simple example application, _raftexample_, is also available to help illustrat
|
||||
how to use this package in practice:
|
||||
https://github.com/etcd-io/etcd/tree/main/contrib/raftexample
|
||||
|
||||
Usage
|
||||
# Usage
|
||||
|
||||
The primary object in raft is a Node. You either start a Node from scratch
|
||||
using raft.StartNode or start a Node from some initial state using raft.RestartNode.
|
||||
|
||||
To start a node from scratch:
|
||||
|
||||
storage := raft.NewMemoryStorage()
|
||||
c := &Config{
|
||||
ID: 0x01,
|
||||
ElectionTick: 10,
|
||||
HeartbeatTick: 1,
|
||||
Storage: storage,
|
||||
MaxSizePerMsg: 4096,
|
||||
MaxInflightMsgs: 256,
|
||||
}
|
||||
n := raft.StartNode(c, []raft.Peer{{ID: 0x02}, {ID: 0x03}})
|
||||
storage := raft.NewMemoryStorage()
|
||||
c := &Config{
|
||||
ID: 0x01,
|
||||
ElectionTick: 10,
|
||||
HeartbeatTick: 1,
|
||||
Storage: storage,
|
||||
MaxSizePerMsg: 4096,
|
||||
MaxInflightMsgs: 256,
|
||||
}
|
||||
n := raft.StartNode(c, []raft.Peer{{ID: 0x02}, {ID: 0x03}})
|
||||
|
||||
To restart a node from previous state:
|
||||
|
||||
storage := raft.NewMemoryStorage()
|
||||
storage := raft.NewMemoryStorage()
|
||||
|
||||
// recover the in-memory storage from persistent
|
||||
// snapshot, state and entries.
|
||||
storage.ApplySnapshot(snapshot)
|
||||
storage.SetHardState(state)
|
||||
storage.Append(entries)
|
||||
// recover the in-memory storage from persistent
|
||||
// snapshot, state and entries.
|
||||
storage.ApplySnapshot(snapshot)
|
||||
storage.SetHardState(state)
|
||||
storage.Append(entries)
|
||||
|
||||
c := &Config{
|
||||
ID: 0x01,
|
||||
ElectionTick: 10,
|
||||
HeartbeatTick: 1,
|
||||
Storage: storage,
|
||||
MaxSizePerMsg: 4096,
|
||||
MaxInflightMsgs: 256,
|
||||
}
|
||||
c := &Config{
|
||||
ID: 0x01,
|
||||
ElectionTick: 10,
|
||||
HeartbeatTick: 1,
|
||||
Storage: storage,
|
||||
MaxSizePerMsg: 4096,
|
||||
MaxInflightMsgs: 256,
|
||||
}
|
||||
|
||||
// restart raft without peer information.
|
||||
// peer information is already included in the storage.
|
||||
n := raft.RestartNode(c)
|
||||
// restart raft without peer information.
|
||||
// peer information is already included in the storage.
|
||||
n := raft.RestartNode(c)
|
||||
|
||||
Now that you are holding onto a Node you have a few responsibilities:
|
||||
|
||||
@ -120,29 +120,29 @@ represented by an abstract "tick".
|
||||
|
||||
The total state machine handling loop will look something like this:
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.Ticker:
|
||||
n.Tick()
|
||||
case rd := <-s.Node.Ready():
|
||||
saveToStorage(rd.State, rd.Entries, rd.Snapshot)
|
||||
send(rd.Messages)
|
||||
if !raft.IsEmptySnap(rd.Snapshot) {
|
||||
processSnapshot(rd.Snapshot)
|
||||
}
|
||||
for _, entry := range rd.CommittedEntries {
|
||||
process(entry)
|
||||
if entry.Type == raftpb.EntryConfChange {
|
||||
var cc raftpb.ConfChange
|
||||
cc.Unmarshal(entry.Data)
|
||||
s.Node.ApplyConfChange(cc)
|
||||
}
|
||||
}
|
||||
s.Node.Advance()
|
||||
case <-s.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-s.Ticker:
|
||||
n.Tick()
|
||||
case rd := <-s.Node.Ready():
|
||||
saveToStorage(rd.State, rd.Entries, rd.Snapshot)
|
||||
send(rd.Messages)
|
||||
if !raft.IsEmptySnap(rd.Snapshot) {
|
||||
processSnapshot(rd.Snapshot)
|
||||
}
|
||||
for _, entry := range rd.CommittedEntries {
|
||||
process(entry)
|
||||
if entry.Type == raftpb.EntryConfChange {
|
||||
var cc raftpb.ConfChange
|
||||
cc.Unmarshal(entry.Data)
|
||||
s.Node.ApplyConfChange(cc)
|
||||
}
|
||||
}
|
||||
s.Node.Advance()
|
||||
case <-s.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
To propose changes to the state machine from your node take your application
|
||||
data, serialize it into a byte slice and call:
|
||||
@ -169,7 +169,7 @@ given ID MUST be used only once even if the old node has been removed.
|
||||
This means that for example IP addresses make poor node IDs since they
|
||||
may be reused. Node IDs must be non-zero.
|
||||
|
||||
Implementation notes
|
||||
# Implementation notes
|
||||
|
||||
This implementation is up to date with the final Raft thesis
|
||||
(https://github.com/ongardie/dissertation/blob/master/stanford.pdf), although our
|
||||
@ -194,7 +194,7 @@ cannot be removed any more since the cluster cannot make progress.
|
||||
For this reason it is highly recommended to use three or more nodes in
|
||||
every cluster.
|
||||
|
||||
MessageType
|
||||
# MessageType
|
||||
|
||||
Package raft sends and receives message in Protocol Buffer format (defined
|
||||
in raftpb package). Each state (follower, candidate, leader) implements its
|
||||
@ -295,6 +295,5 @@ stale log entries:
|
||||
that the follower that sent this 'MsgUnreachable' is not reachable, often
|
||||
indicating 'MsgApp' is lost. When follower's progress state is replicate,
|
||||
the leader sets it back to probe.
|
||||
|
||||
*/
|
||||
package raft
|
||||
|
13
raft/go.mod
13
raft/go.mod
@ -1,14 +1,17 @@
|
||||
module go.etcd.io/etcd/raft/v3
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 // indirect
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5
|
||||
github.com/cockroachdb/datadriven v1.0.2
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
)
|
||||
|
||||
// Bad imports are sometimes causing attempts to pull that code.
|
||||
|
39
raft/go.sum
39
raft/go.sum
@ -1,19 +1,5 @@
|
||||
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
|
||||
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
|
||||
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@ -21,27 +7,12 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
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=
|
||||
@ -57,11 +28,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
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.0.0-20210403161142-5e06dd20ab57/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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
|
||||
@ -73,7 +42,3 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.8/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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -146,12 +146,14 @@ func TestAppend(t *testing.T) {
|
||||
|
||||
// TestLogMaybeAppend ensures:
|
||||
// If the given (index, term) matches with the existing log:
|
||||
// 1. If an existing entry conflicts with a new one (same index
|
||||
// but different terms), delete the existing entry and all that
|
||||
// follow it
|
||||
// 2.Append any new entries not already in the log
|
||||
// 1. If an existing entry conflicts with a new one (same index
|
||||
// but different terms), delete the existing entry and all that
|
||||
// follow it
|
||||
// 2. Append any new entries not already in the log
|
||||
//
|
||||
// If the given (index, term) does not match with the existing log:
|
||||
// return false
|
||||
//
|
||||
// return false
|
||||
func TestLogMaybeAppend(t *testing.T) {
|
||||
previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
|
||||
lastindex := uint64(3)
|
||||
@ -528,7 +530,7 @@ func TestStableToWithSnap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//TestCompaction ensures that the number of log entries is correct after compactions.
|
||||
// TestCompaction ensures that the number of log entries is correct after compactions.
|
||||
func TestCompaction(t *testing.T) {
|
||||
tests := []struct {
|
||||
lastIndex uint64
|
||||
|
@ -76,7 +76,7 @@ func TestNodeStep(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel and Stop should unblock Step()
|
||||
// TestNodeStepUnblock should Cancel and Stop should unblock Step()
|
||||
func TestNodeStepUnblock(t *testing.T) {
|
||||
// a node without buffer to block step
|
||||
n := &node{
|
||||
@ -959,14 +959,14 @@ func (s *ignoreSizeHintMemStorage) Entries(lo, hi uint64, maxSize uint64) ([]raf
|
||||
// Storage's Entries size limitation is slightly more permissive than Raft's
|
||||
// internal one. The original bug was the following:
|
||||
//
|
||||
// - node learns that index 11 (or 100, doesn't matter) is committed
|
||||
// - nextEnts returns index 1..10 in CommittedEntries due to size limiting. However,
|
||||
// index 10 already exceeds maxBytes, due to a user-provided impl of Entries.
|
||||
// - Commit index gets bumped to 10
|
||||
// - the node persists the HardState, but crashes before applying the entries
|
||||
// - upon restart, the storage returns the same entries, but `slice` takes a different code path
|
||||
// (since it is now called with an upper bound of 10) and removes the last entry.
|
||||
// - Raft emits a HardState with a regressing commit index.
|
||||
// - node learns that index 11 (or 100, doesn't matter) is committed
|
||||
// - nextEnts returns index 1..10 in CommittedEntries due to size limiting. However,
|
||||
// index 10 already exceeds maxBytes, due to a user-provided impl of Entries.
|
||||
// - Commit index gets bumped to 10
|
||||
// - the node persists the HardState, but crashes before applying the entries
|
||||
// - upon restart, the storage returns the same entries, but `slice` takes a different code path
|
||||
// (since it is now called with an upper bound of 10) and removes the last entry.
|
||||
// - Raft emits a HardState with a regressing commit index.
|
||||
//
|
||||
// A simpler version of this test would have the storage return a lot less entries than dictated
|
||||
// by maxSize (for example, exactly one entry) after the restart, resulting in a larger regression.
|
||||
|
@ -1207,8 +1207,8 @@ func TestPastElectionTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the Step function ignores the message from old term and does not pass it to the
|
||||
// actual stepX function.
|
||||
// TestStepIgnoreOldTermMsg to ensure that the Step function ignores the message
|
||||
// from old term and does not pass it to the actual stepX function.
|
||||
func TestStepIgnoreOldTermMsg(t *testing.T) {
|
||||
called := false
|
||||
fakeStep := func(r *raft, m pb.Message) error {
|
||||
@ -1225,10 +1225,10 @@ func TestStepIgnoreOldTermMsg(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestHandleMsgApp ensures:
|
||||
// 1. Reply false if log doesn’t contain an entry at prevLogIndex whose term matches prevLogTerm.
|
||||
// 2. If an existing entry conflicts with a new one (same index but different terms),
|
||||
// delete the existing entry and all that follow it; append any new entries not already in the log.
|
||||
// 3. If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry).
|
||||
// 1. Reply false if log doesn’t contain an entry at prevLogIndex whose term matches prevLogTerm.
|
||||
// 2. If an existing entry conflicts with a new one (same index but different terms),
|
||||
// delete the existing entry and all that follow it; append any new entries not already in the log.
|
||||
// 3. If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry).
|
||||
func TestHandleMsgApp(t *testing.T) {
|
||||
tests := []struct {
|
||||
m pb.Message
|
||||
@ -2479,7 +2479,7 @@ func TestLeaderAppResp(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// When the leader receives a heartbeat tick, it should
|
||||
// TestBcastBeat is when the leader receives a heartbeat tick, it should
|
||||
// send a MsgHeartbeat with m.Index = 0, m.LogTerm=0 and empty entries.
|
||||
func TestBcastBeat(t *testing.T) {
|
||||
offset := uint64(1000)
|
||||
@ -2539,7 +2539,7 @@ func TestBcastBeat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// tests the output of the state machine when receiving MsgBeat
|
||||
// TestRecvMsgBeat tests the output of the state machine when receiving MsgBeat
|
||||
func TestRecvMsgBeat(t *testing.T) {
|
||||
tests := []struct {
|
||||
state StateType
|
||||
@ -2818,7 +2818,7 @@ func TestRestoreWithLearner(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests if outgoing voter can receive and apply snapshot correctly.
|
||||
// TestRestoreWithVotersOutgoing tests if outgoing voter can receive and apply snapshot correctly.
|
||||
func TestRestoreWithVotersOutgoing(t *testing.T) {
|
||||
s := pb.Snapshot{
|
||||
Metadata: pb.SnapshotMetadata{
|
||||
@ -4306,12 +4306,12 @@ func testConfChangeCheckBeforeCampaign(t *testing.T, v2 bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests if unapplied ConfChange is checked before campaign.
|
||||
// TestConfChangeCheckBeforeCampaign tests if unapplied ConfChange is checked before campaign.
|
||||
func TestConfChangeCheckBeforeCampaign(t *testing.T) {
|
||||
testConfChangeCheckBeforeCampaign(t, false)
|
||||
}
|
||||
|
||||
// Tests if unapplied ConfChangeV2 is checked before campaign.
|
||||
// TestConfChangeV2CheckBeforeCampaign tests if unapplied ConfChangeV2 is checked before campaign.
|
||||
func TestConfChangeV2CheckBeforeCampaign(t *testing.T) {
|
||||
testConfChangeCheckBeforeCampaign(t, true)
|
||||
}
|
||||
|
@ -36,22 +36,22 @@ type rawNodeAdapter struct {
|
||||
|
||||
var _ Node = (*rawNodeAdapter)(nil)
|
||||
|
||||
// Node specifies lead, which is pointless, can just be filled in.
|
||||
// TransferLeadership is to test when node specifies lead, which is pointless, can just be filled in.
|
||||
func (a *rawNodeAdapter) TransferLeadership(ctx context.Context, lead, transferee uint64) {
|
||||
a.RawNode.TransferLeader(transferee)
|
||||
}
|
||||
|
||||
// Node has a goroutine, RawNode doesn't need this.
|
||||
// Stop when node has a goroutine, RawNode doesn't need this.
|
||||
func (a *rawNodeAdapter) Stop() {}
|
||||
|
||||
// RawNode returns a *Status.
|
||||
// Status returns RawNode's status as *Status.
|
||||
func (a *rawNodeAdapter) Status() Status { return a.RawNode.Status() }
|
||||
|
||||
// RawNode takes a Ready. It doesn't really have to do that I think? It can hold on
|
||||
// Advance is when RawNode takes a Ready. It doesn't really have to do that I think? It can hold on
|
||||
// to it internally. But maybe that approach is frail.
|
||||
func (a *rawNodeAdapter) Advance() { a.RawNode.Advance(Ready{}) }
|
||||
|
||||
// RawNode returns a Ready, not a chan of one.
|
||||
// Ready when RawNode returns a Ready, not a chan of one.
|
||||
func (a *rawNodeAdapter) Ready() <-chan Ready { return nil }
|
||||
|
||||
// Node takes more contexts. Easy enough to fix.
|
||||
@ -868,17 +868,17 @@ func TestRawNodeStatus(t *testing.T) {
|
||||
// TestNodeCommitPaginationAfterRestart. The anomaly here was even worse as the
|
||||
// Raft group would forget to apply entries:
|
||||
//
|
||||
// - node learns that index 11 is committed
|
||||
// - nextEnts returns index 1..10 in CommittedEntries (but index 10 already
|
||||
// exceeds maxBytes), which isn't noticed internally by Raft
|
||||
// - Commit index gets bumped to 10
|
||||
// - the node persists the HardState, but crashes before applying the entries
|
||||
// - upon restart, the storage returns the same entries, but `slice` takes a
|
||||
// different code path and removes the last entry.
|
||||
// - Raft does not emit a HardState, but when the app calls Advance(), it bumps
|
||||
// its internal applied index cursor to 10 (when it should be 9)
|
||||
// - the next Ready asks the app to apply index 11 (omitting index 10), losing a
|
||||
// write.
|
||||
// - node learns that index 11 is committed
|
||||
// - nextEnts returns index 1..10 in CommittedEntries (but index 10 already
|
||||
// exceeds maxBytes), which isn't noticed internally by Raft
|
||||
// - Commit index gets bumped to 10
|
||||
// - the node persists the HardState, but crashes before applying the entries
|
||||
// - upon restart, the storage returns the same entries, but `slice` takes a
|
||||
// different code path and removes the last entry.
|
||||
// - Raft does not emit a HardState, but when the app calls Advance(), it bumps
|
||||
// its internal applied index cursor to 10 (when it should be 9)
|
||||
// - the next Ready asks the app to apply index 11 (omitting index 10), losing a
|
||||
// write.
|
||||
func TestRawNodeCommitPaginationAfterRestart(t *testing.T) {
|
||||
s := &ignoreSizeHintMemStorage{
|
||||
MemoryStorage: newTestMemoryStorage(withPeers(1)),
|
||||
|
@ -1,19 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
source ./scripts/test_lib.sh
|
||||
|
||||
VER=$1
|
||||
VER=${1:-}
|
||||
REPOSITORY="${REPOSITORY:-git@github.com:etcd-io/etcd.git}"
|
||||
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
if [ -z "${VER}" ]; then
|
||||
echo "Usage: ${0} VERSION" >> /dev/stderr
|
||||
exit 255
|
||||
fi
|
||||
|
||||
set -u
|
||||
|
||||
function setup_env {
|
||||
local ver=${1}
|
||||
@ -25,9 +23,7 @@ function setup_env {
|
||||
|
||||
pushd "${proj}" >/dev/null
|
||||
run git fetch --all
|
||||
git_assert_branch_in_sync || exit 2
|
||||
run git checkout "${ver}"
|
||||
git_assert_branch_in_sync || exit 2
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
@ -41,7 +37,7 @@ function package {
|
||||
srcdir="${ccdir}"
|
||||
fi
|
||||
local ext=""
|
||||
if [ "${GOOS}" == "windows" ]; then
|
||||
if [ "${GOOS:-}" == "windows" ]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
for bin in etcd etcdctl etcdutl; do
|
||||
@ -63,7 +59,7 @@ function main {
|
||||
cd release
|
||||
setup_env "${VER}" "${proj}"
|
||||
|
||||
tarcmd=tar
|
||||
local tarcmd=tar
|
||||
if [[ $(go env GOOS) == "darwin" ]]; then
|
||||
echo "Please use linux machine for release builds."
|
||||
exit 1
|
||||
@ -87,7 +83,7 @@ function main {
|
||||
export GOARCH=${TARGET_ARCH}
|
||||
|
||||
pushd etcd >/dev/null
|
||||
GO_LDFLAGS="-s" ./build.sh
|
||||
GO_LDFLAGS="-s -w" ./build.sh
|
||||
popd >/dev/null
|
||||
|
||||
TARGET="etcd-${VER}-${GOOS}-${GOARCH}"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 VERSION" >&2
|
||||
@ -11,7 +11,7 @@ ARCH=$(go env GOARCH)
|
||||
VERSION="${1}-${ARCH}"
|
||||
DOCKERFILE="Dockerfile-release.${ARCH}"
|
||||
|
||||
if [ -z "${BINARYDIR}" ]; then
|
||||
if [ -z "${BINARYDIR:-}" ]; then
|
||||
RELEASE="etcd-${1}"-$(go env GOOS)-$(go env GOARCH)
|
||||
BINARYDIR="${RELEASE}"
|
||||
TARFILE="${RELEASE}.tar.gz"
|
||||
@ -34,9 +34,11 @@ cp "${BINARYDIR}"/etcd "${BINARYDIR}"/etcdctl "${BINARYDIR}"/etcdutl "${IMAGEDIR
|
||||
|
||||
cat ./"${DOCKERFILE}" > "${IMAGEDIR}"/Dockerfile
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
docker build -t "gcr.io/etcd-development/etcd:${VERSION}" "${IMAGEDIR}"
|
||||
docker build -t "quay.io/coreos/etcd:${VERSION}" "${IMAGEDIR}"
|
||||
if [ -z "${TAG:-}" ]; then
|
||||
# Fix incorrect image "Architecture" using buildkit
|
||||
# From https://stackoverflow.com/q/72144329/
|
||||
DOCKER_BUILDKIT=1 docker build -t "gcr.io/etcd-development/etcd:${VERSION}" "${IMAGEDIR}"
|
||||
DOCKER_BUILDKIT=1 docker build -t "quay.io/coreos/etcd:${VERSION}" "${IMAGEDIR}"
|
||||
else
|
||||
docker build -t "${TAG}:${VERSION}" "${IMAGEDIR}"
|
||||
fi
|
||||
|
@ -3,11 +3,11 @@
|
||||
# Build all release binaries and images to directory ./release.
|
||||
# Run from repository root.
|
||||
#
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
source ./scripts/test_lib.sh
|
||||
|
||||
VERSION=$1
|
||||
VERSION=${1:-}
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "Usage: ${0} VERSION" >> /dev/stderr
|
||||
exit 255
|
||||
|
@ -41,6 +41,7 @@ help() {
|
||||
echo " flags:"
|
||||
echo " --no-upload: skip gs://etcd binary artifact uploads."
|
||||
echo " --no-docker-push: skip docker image pushes."
|
||||
echo " --in-place: build binaries using current branch."
|
||||
echo ""
|
||||
echo "One can perform a (dry-run) test release from any (uncommitted) branch using:"
|
||||
echo " DRY_RUN=true REPOSITORY=\`pwd\` BRANCH='local-branch-name' ./scripts/release 3.5.0-foobar.2"
|
||||
@ -54,8 +55,15 @@ main() {
|
||||
fi
|
||||
RELEASE_VERSION="v${VERSION}"
|
||||
MINOR_VERSION=$(echo "${VERSION}" | cut -d. -f 1-2)
|
||||
BRANCH=${BRANCH:-"release-${MINOR_VERSION}"}
|
||||
REPOSITORY=${REPOSITORY:-"https://github.com/etcd-io/etcd.git"}
|
||||
|
||||
if [ "${IN_PLACE}" == 1 ]; then
|
||||
# Trigger release in current branch
|
||||
REPOSITORY=$(pwd)
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
else
|
||||
REPOSITORY=${REPOSITORY:-"https://github.com/etcd-io/etcd.git"}
|
||||
BRANCH=${BRANCH:-"release-${MINOR_VERSION}"}
|
||||
fi
|
||||
|
||||
log_warning "DRY_RUN=${DRY_RUN}"
|
||||
log_callout "RELEASE_VERSION=${RELEASE_VERSION}"
|
||||
@ -78,19 +86,20 @@ main() {
|
||||
# Set up release directory.
|
||||
local reldir="/tmp/etcd-release-${VERSION}"
|
||||
log_callout "Preparing temporary directory: ${reldir}"
|
||||
if [ ! -d "${reldir}/etcd" ]; then
|
||||
if [ ! -d "${reldir}/etcd" ] && [ "${IN_PLACE}" == 0 ]; then
|
||||
mkdir -p "${reldir}"
|
||||
cd "${reldir}"
|
||||
run git clone "${REPOSITORY}" --branch "${BRANCH}"
|
||||
run cd "${reldir}/etcd" || exit 2
|
||||
run git checkout "${BRANCH}" || exit 2
|
||||
run git pull origin
|
||||
|
||||
git_assert_branch_in_sync || exit 2
|
||||
fi
|
||||
run cd "${reldir}/etcd" || exit 2
|
||||
|
||||
# mark local directory as root for test_lib scripts executions
|
||||
set_root_dir
|
||||
|
||||
run git checkout "${BRANCH}" || exit 2
|
||||
run git pull origin
|
||||
git_assert_branch_in_sync || exit 2
|
||||
|
||||
# If a release version tag already exists, use it.
|
||||
local remote_tag_exists
|
||||
remote_tag_exists=$(run git ls-remote origin "refs/tags/${RELEASE_VERSION}" | grep -c "${RELEASE_VERSION}" || true)
|
||||
@ -101,6 +110,7 @@ main() {
|
||||
fi
|
||||
|
||||
# Check go version.
|
||||
log_callout "Check go version"
|
||||
local go_version current_go_version
|
||||
go_version="go$(grep go-version .github/workflows/tests.yaml | awk '{print $2}' | tr -d '"')"
|
||||
current_go_version=$(go version | awk '{ print $3 }')
|
||||
@ -110,6 +120,7 @@ main() {
|
||||
fi
|
||||
|
||||
# If the release tag does not already exist remotely, create it.
|
||||
log_callout "Create tag if not present"
|
||||
if [ "${remote_tag_exists}" -eq 0 ]; then
|
||||
# Bump version/version.go to release version.
|
||||
local source_version
|
||||
@ -147,7 +158,7 @@ main() {
|
||||
fi
|
||||
|
||||
# Push the version change if it's not already been pushed.
|
||||
if [ "$DRY_RUN" != "true" ] && [ "$(git rev-list --count "origin/${BRANCH}..${BRANCH}")" -gt 0 ]; then
|
||||
if [ "${DRY_RUN}" != "true" ] && [ "$(git rev-list --count "origin/${BRANCH}..${BRANCH}")" -gt 0 ]; then
|
||||
read -p "Push version bump up to ${VERSION} to '$(git remote get-url origin)' [y/N]? " -r confirm
|
||||
[[ "${confirm,,}" == "y" ]] || exit 1
|
||||
maybe_run git push
|
||||
@ -161,14 +172,23 @@ main() {
|
||||
REMOTE_REPO="origin" push_mod_tags_cmd
|
||||
fi
|
||||
|
||||
# Verify the version tag is on the right branch
|
||||
local branch=$(git for-each-ref --contains "${RELEASE_VERSION}" --format="%(refname)" 'refs/heads' | cut -d '/' -f 3)
|
||||
if [ "${branch}" != "${BRANCH}" ]; then
|
||||
log_error "Error: Git tag ${RELEASE_VERSION} should be on branch '${BRANCH}' but is on '${branch}'"
|
||||
exit 1
|
||||
if [ "${IN_PLACE}" == 0 ]; then
|
||||
# Tried with `local branch=$(git branch -a --contains tags/"${RELEASE_VERSION}")`
|
||||
# so as to work with both current branch and main/release-3.X.
|
||||
# But got error below on current branch mode,
|
||||
# Error: Git tag v3.6.99 should be on branch '* (HEAD detached at pull/14860/merge)' but is on '* (HEAD detached from pull/14860/merge)'
|
||||
#
|
||||
# Verify the version tag is on the right branch
|
||||
# shellcheck disable=SC2155
|
||||
local branch=$(git for-each-ref --contains "${RELEASE_VERSION}" --format="%(refname)" 'refs/heads' | cut -d '/' -f 3)
|
||||
if [ "${branch}" != "${BRANCH}" ]; then
|
||||
log_error "Error: Git tag ${RELEASE_VERSION} should be on branch '${BRANCH}' but is on '${branch}'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log_callout "Verify the latest commit has the version tag"
|
||||
# Verify the latest commit has the version tag
|
||||
# shellcheck disable=SC2155
|
||||
local tag="$(git describe --exact-match HEAD)"
|
||||
@ -177,6 +197,7 @@ main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_callout "Verify the work space is clean"
|
||||
# Verify the clean working tree
|
||||
# shellcheck disable=SC2155
|
||||
local diff="$(git diff HEAD --stat)"
|
||||
@ -212,7 +233,7 @@ main() {
|
||||
fi
|
||||
|
||||
# Upload artifacts.
|
||||
if [ "${NO_UPLOAD}" == 1 ]; then
|
||||
if [ "${DRY_RUN}" == "true" ] || [ "${NO_UPLOAD}" == 1 ]; then
|
||||
log_callout "Skipping artifact upload to gs://etcd. --no-upload flat is set."
|
||||
else
|
||||
read -p "Upload etcd ${RELEASE_VERSION} release artifacts to gs://etcd [y/N]? " -r confirm
|
||||
@ -224,7 +245,7 @@ main() {
|
||||
fi
|
||||
|
||||
# Push images.
|
||||
if [ "${NO_DOCKER_PUSH}" == 1 ]; then
|
||||
if [ "${DRY_RUN}" == "true" ] || [ "${NO_DOCKER_PUSH}" == 1 ]; then
|
||||
log_callout "Skipping docker push. --no-docker-push flat is set."
|
||||
else
|
||||
read -p "Publish etcd ${RELEASE_VERSION} docker images to quay.io [y/N]? " -r confirm
|
||||
@ -303,6 +324,7 @@ main() {
|
||||
POSITIONAL=()
|
||||
NO_UPLOAD=0
|
||||
NO_DOCKER_PUSH=0
|
||||
IN_PLACE=0
|
||||
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
@ -311,6 +333,10 @@ while test $# -gt 0; do
|
||||
help
|
||||
exit 0
|
||||
;;
|
||||
--in-place)
|
||||
IN_PLACE=1
|
||||
shift
|
||||
;;
|
||||
--no-upload)
|
||||
NO_UPLOAD=1
|
||||
shift
|
||||
@ -332,4 +358,11 @@ if [[ ! $# -eq 1 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Note that we shouldn't upload artifacts in --in-place mode, so it
|
||||
# must be called with DRY_RUN=true
|
||||
if [ "${DRY_RUN}" != "true" ] && [ "${IN_PLACE}" == 1 ]; then
|
||||
log_error "--in-place should only be called with DRY_RUN=true"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
main "$1"
|
||||
|
93
scripts/test_images.sh
Executable file
93
scripts/test_images.sh
Executable file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
source ./scripts/test_lib.sh
|
||||
|
||||
function startContainer {
|
||||
# run docker in the background
|
||||
docker run -d --rm --name "${RUN_NAME}" "${IMAGE}"
|
||||
|
||||
# wait for etcd daemon to bootstrap
|
||||
sleep 5
|
||||
}
|
||||
|
||||
function runVersionCheck {
|
||||
Out=$(docker run --rm "${IMAGE}" "${@}")
|
||||
foundVersion=$(echo "$Out" | head -1 | rev | cut -d" " -f 1 | rev )
|
||||
if [[ "${foundVersion}" != "${VERSION}" ]]; then
|
||||
echo "error: Invalid Version. Got $foundVersion, expected $VERSION. Error: $Out"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Can't proceed without docker
|
||||
if ! command -v docker >/dev/null; then
|
||||
log_error "cannot find docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# You can't run darwin binaries in linux containers
|
||||
if [[ $(go env GOOS) == "darwin" ]]; then
|
||||
echo "Please use linux machine for release builds."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pick defaults based on release workflow
|
||||
ARCH=$(go env GOARCH)
|
||||
REPOSITARY=${REPOSITARY:-"gcr.io/etcd-development/etcd"}
|
||||
if [ -n "$VERSION" ]; then
|
||||
# Expected Format: v3.6.99-amd64
|
||||
TAG=v"${VERSION}"-"${ARCH}"
|
||||
else
|
||||
echo "Terminating test, VERSION not supplied"
|
||||
exit 1
|
||||
fi
|
||||
IMAGE=${IMAGE:-"${REPOSITARY}:${TAG}"}
|
||||
|
||||
# ETCD related values
|
||||
RUN_NAME="test_etcd"
|
||||
KEY="foo"
|
||||
VALUE="bar"
|
||||
|
||||
if [[ "$(docker images -q "${IMAGE}" 2> /dev/null)" == "" ]]; then
|
||||
echo "${IMAGE} not present locally"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Version check
|
||||
runVersionCheck "/usr/local/bin/etcd" "--version"
|
||||
runVersionCheck "/usr/local/bin/etcdctl" "version"
|
||||
runVersionCheck "/usr/local/bin/etcdutl" "version"
|
||||
|
||||
startContainer
|
||||
# stop container
|
||||
trap 'docker stop "${RUN_NAME}"' EXIT
|
||||
|
||||
|
||||
# Put/Get check
|
||||
PUT=$(docker exec "${RUN_NAME}" /usr/local/bin/etcdctl put "${KEY}" "${VALUE}")
|
||||
if [ "${PUT}" != "OK" ]; then
|
||||
echo "Problem with Putting in etcd"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GET=$(docker exec "${RUN_NAME}" /usr/local/bin/etcdctl get "$KEY" --print-value-only)
|
||||
if [ "${GET}" != "${VALUE}" ]; then
|
||||
echo "Problem with getting foo bar in etcd. Got ${GET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Succesfully tested etcd local image ${TAG}"
|
||||
|
||||
for TARGET_ARCH in "amd64" "arm64" "ppc64le" "s390x"; do
|
||||
ARCH_TAG=v"${VERSION}"-"${TARGET_ARCH}"
|
||||
IMG_ARCH=$(docker inspect --format '{{.Architecture}}' "${REPOSITARY}:${ARCH_TAG}")
|
||||
if [ "${IMG_ARCH}" != "$TARGET_ARCH" ];then
|
||||
echo "Incorrect docker image architecture"
|
||||
exit 1
|
||||
fi
|
||||
echo "Correct Architecture ${ARCH_TAG}"
|
||||
done
|
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_MODULE="go.etcd.io/etcd"
|
||||
|
||||
if [[ "$(go list)" != "${ROOT_MODULE}/v3" ]]; then
|
||||
@ -71,7 +73,8 @@ function relativePath {
|
||||
local commonPart=$source
|
||||
local result=""
|
||||
|
||||
while [[ "${target#$commonPart}" == "${target}" ]]; do
|
||||
# Refer to https://www.shellcheck.net/wiki/SC2295
|
||||
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")"
|
||||
@ -90,7 +93,8 @@ function relativePath {
|
||||
|
||||
# since we now have identified the common part,
|
||||
# compute the non-common part
|
||||
local forwardPart="${target#$commonPart}"
|
||||
# Refer to https://www.shellcheck.net/wiki/SC2295
|
||||
local forwardPart="${target#"$commonPart"}"
|
||||
|
||||
# and now stick all parts together
|
||||
if [[ -n $result ]] && [[ -n $forwardPart ]]; then
|
||||
@ -105,10 +109,10 @@ function relativePath {
|
||||
|
||||
#### Discovery of files/packages within a go module #####
|
||||
|
||||
# go_srcs_in_module [package]
|
||||
# go_srcs_in_module
|
||||
# returns list of all not-generated go sources in the current (dir) module.
|
||||
function go_srcs_in_module {
|
||||
go fmt -n "$1" | grep -Eo "([^ ]*)$" | grep -vE "(\\_test.go|\\.pb\\.go|\\.pb\\.gw.go)"
|
||||
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]
|
||||
@ -169,7 +173,7 @@ function module_dirs() {
|
||||
|
||||
# maybe_run [cmd...] runs given command depending on the DRY_RUN flag.
|
||||
function maybe_run() {
|
||||
if ${DRY_RUN}; then
|
||||
if ${DRY_RUN:-}; then
|
||||
log_warning -e "# DRY_RUN:\\n % ${*}"
|
||||
else
|
||||
run "${@}"
|
||||
@ -241,7 +245,7 @@ function go_test {
|
||||
|
||||
local goTestFlags=""
|
||||
local goTestEnv=""
|
||||
if [ "${VERBOSE}" == "1" ]; then
|
||||
if [ "${VERBOSE:-}" == "1" ]; then
|
||||
goTestFlags="-v"
|
||||
fi
|
||||
|
||||
@ -308,16 +312,25 @@ fi
|
||||
|
||||
# tool_get_bin [tool] - returns absolute path to a tool binary (or returns error)
|
||||
function tool_get_bin {
|
||||
tool_exists "gobin" "GO111MODULE=off go get github.com/myitcv/gobin" || return 2
|
||||
|
||||
local tool="$1"
|
||||
local pkg_part="$1"
|
||||
if [[ "$tool" == *"@"* ]]; then
|
||||
pkg_part=$(echo "${tool}" | cut -d'@' -f1)
|
||||
# shellcheck disable=SC2086
|
||||
run gobin ${GOBINARGS:-} -p "${tool}" || return 2
|
||||
run go install ${GOBINARGS:-} "${tool}" || return 2
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
run_for_module ./tools/mod run gobin ${GOBINARGS:-} -p -m --mod=readonly "${tool}" || return 2
|
||||
run_for_module ./tools/mod run go install ${GOBINARGS:-} "${tool}" || return 2
|
||||
fi
|
||||
|
||||
# remove the version suffix, such as removing "/v3" from "go.etcd.io/etcd/v3".
|
||||
local cmd_base_name
|
||||
cmd_base_name=$(basename "${pkg_part}")
|
||||
if [[ ${cmd_base_name} =~ ^v[0-9]*$ ]]; then
|
||||
pkg_part=$(dirname "${pkg_part}")
|
||||
fi
|
||||
|
||||
run_for_module ./tools/mod go list -f '{{.Target}}' "${pkg_part}"
|
||||
}
|
||||
|
||||
# tool_pkg_dir [pkg] - returns absolute path to a directory that stores given pkg.
|
||||
@ -354,14 +367,16 @@ function assert_no_git_modifications {
|
||||
# - no differencing commits in relation to the origin/$branch
|
||||
function git_assert_branch_in_sync {
|
||||
local branch
|
||||
branch=$(run git rev-parse --abbrev-ref HEAD)
|
||||
# TODO: When git 2.22 popular, change to:
|
||||
# branch=$(git branch --show-current)
|
||||
branch=$(run git rev-parse --abbrev-ref HEAD)
|
||||
log_callout "Verify the current branch '${branch}' is clean"
|
||||
if [[ $(run git status --porcelain --untracked-files=no) ]]; then
|
||||
log_error "The workspace in '$(pwd)' for branch: ${branch} has uncommitted changes"
|
||||
log_error "Consider cleaning up / renaming this directory or (cd $(pwd) && git reset --hard)"
|
||||
return 2
|
||||
fi
|
||||
log_callout "Verify the current branch '${branch}' is in sync with the 'origin/${branch}'"
|
||||
if [ -n "${branch}" ]; then
|
||||
ref_local=$(run git rev-parse "${branch}")
|
||||
ref_origin=$(run git rev-parse "origin/${branch}")
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
jwt "github.com/form3tech-oss/jwt-go"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -42,7 +42,7 @@ func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInf
|
||||
// rev isn't used in JWT, it is only used in simple token
|
||||
var (
|
||||
username string
|
||||
revision uint64
|
||||
revision float64
|
||||
)
|
||||
|
||||
parsed, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
|
||||
@ -74,10 +74,19 @@ func (t *tokenJWT) info(ctx context.Context, token string, rev uint64) (*AuthInf
|
||||
return nil, false
|
||||
}
|
||||
|
||||
username = claims["username"].(string)
|
||||
revision = uint64(claims["revision"].(float64))
|
||||
username, ok = claims["username"].(string)
|
||||
if !ok {
|
||||
t.lg.Warn("failed to obtain user claims from jwt token")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &AuthInfo{Username: username, Revision: revision}, true
|
||||
revision, ok = claims["revision"].(float64)
|
||||
if !ok {
|
||||
t.lg.Warn("failed to obtain revision claims from jwt token")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &AuthInfo{Username: username, Revision: uint64(revision)}, true
|
||||
}
|
||||
|
||||
func (t *tokenJWT) assign(ctx context.Context, username string, revision uint64) (string, error) {
|
||||
|
@ -18,7 +18,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -202,3 +205,75 @@ func TestJWTBad(t *testing.T) {
|
||||
func testJWTOpts() string {
|
||||
return fmt.Sprintf("%s,pub-key=%s,priv-key=%s,sign-method=RS256", tokenTypeJWT, jwtRSAPubKey, jwtRSAPrivKey)
|
||||
}
|
||||
|
||||
func TestJWTTokenWithMissingFields(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
username string // An empty string means not present
|
||||
revision uint64 // 0 means not present
|
||||
expectValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid token",
|
||||
username: "hello",
|
||||
revision: 100,
|
||||
expectValid: true,
|
||||
},
|
||||
{
|
||||
name: "no username",
|
||||
username: "",
|
||||
revision: 100,
|
||||
expectValid: false,
|
||||
},
|
||||
{
|
||||
name: "no revision",
|
||||
username: "hello",
|
||||
revision: 0,
|
||||
expectValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
optsMap := map[string]string{
|
||||
"priv-key": jwtRSAPrivKey,
|
||||
"sign-method": "RS256",
|
||||
"ttl": "1h",
|
||||
}
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// prepare claims
|
||||
claims := jwt.MapClaims{
|
||||
"exp": time.Now().Add(time.Hour).Unix(),
|
||||
}
|
||||
if tc.username != "" {
|
||||
claims["username"] = tc.username
|
||||
}
|
||||
if tc.revision != 0 {
|
||||
claims["revision"] = tc.revision
|
||||
}
|
||||
|
||||
// generate a JWT token with the given claims
|
||||
var opts jwtOptions
|
||||
err := opts.ParseWithDefaults(optsMap)
|
||||
require.NoError(t, err)
|
||||
key, err := opts.Key()
|
||||
require.NoError(t, err)
|
||||
|
||||
tk := jwt.NewWithClaims(opts.SignMethod, claims)
|
||||
token, err := tk.SignedString(key)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify the token
|
||||
jwtProvider, err := newTokenProviderJWT(zap.NewNop(), optsMap)
|
||||
require.NoError(t, err)
|
||||
ai, ok := jwtProvider.info(context.TODO(), token, 123)
|
||||
|
||||
require.Equal(t, tc.expectValid, ok)
|
||||
if ok {
|
||||
require.Equal(t, tc.username, ai.Username)
|
||||
require.Equal(t, tc.revision, ai.Revision)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
jwt "github.com/form3tech-oss/jwt-go"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -76,8 +76,10 @@ func checkKeyInterval(
|
||||
cachedPerms *unifiedRangePermissions,
|
||||
key, rangeEnd []byte,
|
||||
permtyp authpb.Permission_Type) bool {
|
||||
if len(rangeEnd) == 1 && rangeEnd[0] == 0 {
|
||||
if isOpenEnded(rangeEnd) {
|
||||
rangeEnd = nil
|
||||
// nil rangeEnd will be converetd to []byte{}, the largest element of BytesAffineComparable,
|
||||
// in NewBytesAffineInterval().
|
||||
}
|
||||
|
||||
ivl := adt.NewBytesAffineInterval(key, rangeEnd)
|
||||
@ -153,3 +155,51 @@ type unifiedRangePermissions struct {
|
||||
readPerms adt.IntervalTree
|
||||
writePerms adt.IntervalTree
|
||||
}
|
||||
|
||||
// Constraints related to key range
|
||||
// Assumptions:
|
||||
// a1. key must be non-nil
|
||||
// a2. []byte{} (in the case of string, "") is not a valid key of etcd
|
||||
// For representing an open-ended range, BytesAffineComparable uses []byte{} as the largest element.
|
||||
// a3. []byte{0x00} is the minimum valid etcd key
|
||||
//
|
||||
// Based on the above assumptions, key and rangeEnd must follow below rules:
|
||||
// b1. for representing a single key point, rangeEnd should be nil or zero length byte array (in the case of string, "")
|
||||
// Rule a2 guarantees that (X, []byte{}) for any X is not a valid range. So such ranges can be used for representing
|
||||
// a single key permission.
|
||||
//
|
||||
// b2. key range with upper limit, like (X, Y), larger or equal to X and smaller than Y
|
||||
//
|
||||
// b3. key range with open-ended, like (X, <open ended>), is represented like (X, []byte{0x00})
|
||||
// Because of rule a3, if we have (X, []byte{0x00}), such a range represents an empty range and makes no sense to have
|
||||
// such a permission. So we use []byte{0x00} for representing an open-ended permission.
|
||||
// Note that rangeEnd with []byte{0x00} will be converted into []byte{} before inserted into the interval tree
|
||||
// (rule a2 ensures that this is the largest element).
|
||||
// Special range like key = []byte{0x00} and rangeEnd = []byte{0x00} is treated as a range which matches with all keys.
|
||||
//
|
||||
// Treating a range whose rangeEnd with []byte{0x00} as an open-ended comes from the rules of Range() and Watch() API.
|
||||
|
||||
func isOpenEnded(rangeEnd []byte) bool { // check rule b3
|
||||
return len(rangeEnd) == 1 && rangeEnd[0] == 0
|
||||
}
|
||||
|
||||
func isValidPermissionRange(key, rangeEnd []byte) bool {
|
||||
if len(key) == 0 {
|
||||
return false
|
||||
}
|
||||
if rangeEnd == nil || len(rangeEnd) == 0 { // ensure rule b1
|
||||
return true
|
||||
}
|
||||
|
||||
begin := adt.BytesAffineComparable(key)
|
||||
end := adt.BytesAffineComparable(rangeEnd)
|
||||
if begin.Compare(end) == -1 { // rule b2
|
||||
return true
|
||||
}
|
||||
|
||||
if isOpenEnded(rangeEnd) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -45,6 +45,26 @@ func TestRangePermission(t *testing.T) {
|
||||
[]byte("a"), []byte("f"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte("c"), []byte("f"))},
|
||||
[]byte("a"), []byte{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte{})},
|
||||
[]byte("a"), []byte{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte("a"), []byte{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte{0x00}, []byte{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
@ -86,6 +106,16 @@ func TestKeyPermission(t *testing.T) {
|
||||
[]byte("f"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte("c"), []byte{})},
|
||||
[]byte("f"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]adt.Interval{adt.NewBytesAffineInterval([]byte("a"), []byte("d")), adt.NewBytesAffineInterval([]byte("a"), []byte("b")), adt.NewBytesAffineInterval([]byte{0x00}, []byte{})},
|
||||
[]byte("f"),
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
@ -100,3 +130,88 @@ func TestKeyPermission(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangeCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key []byte
|
||||
rangeEnd []byte
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid single key",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte(""),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid single key",
|
||||
key: []byte("a"),
|
||||
rangeEnd: nil,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, key < rangeEnd",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("b"),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid empty key range, key == rangeEnd",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid empty key range, key > rangeEnd",
|
||||
key: []byte("b"),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte("a"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key range, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte(""),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid key range, key must not be \"\"",
|
||||
key: []byte(""),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "valid single key (not useful in practice)",
|
||||
key: []byte("\x00"),
|
||||
rangeEnd: []byte(""),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, larger or equals to \"a\"",
|
||||
key: []byte("a"),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid key range, which includes all keys",
|
||||
key: []byte("\x00"),
|
||||
rangeEnd: []byte("\x00"),
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := isValidPermissionRange(tt.key, tt.rangeEnd)
|
||||
if result != tt.want {
|
||||
t.Errorf("#%d: result=%t, want=%t", i, result, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -370,6 +370,7 @@ func (as *authStore) Recover(be backend.Backend) {
|
||||
}
|
||||
|
||||
as.setRevision(getRevision(tx))
|
||||
as.refreshRangePermCache(tx)
|
||||
|
||||
tx.Unlock()
|
||||
|
||||
@ -478,7 +479,8 @@ func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*p
|
||||
var password []byte
|
||||
var err error
|
||||
|
||||
if !user.Options.NoPassword {
|
||||
// Backward compatible with old versions of etcd, user options is nil
|
||||
if user.Options == nil || !user.Options.NoPassword {
|
||||
password, err = as.selectPassword(r.Password, r.HashedPassword)
|
||||
if err != nil {
|
||||
return nil, ErrNoPasswordUser
|
||||
@ -790,6 +792,9 @@ func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (
|
||||
if r.Perm == nil {
|
||||
return nil, ErrPermissionNotGiven
|
||||
}
|
||||
if !isValidPermissionRange(r.Perm.Key, r.Perm.RangeEnd) {
|
||||
return nil, ErrInvalidAuthMgmt
|
||||
}
|
||||
|
||||
tx := as.be.BatchTx()
|
||||
tx.LockInsideApply()
|
||||
|
@ -17,6 +17,7 @@ package auth
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -74,7 +75,7 @@ func TestNewAuthStoreRevision(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewAuthStoreBryptCost ensures that NewAuthStore uses default when given bcrypt-cost is invalid
|
||||
// TestNewAuthStoreBcryptCost ensures that NewAuthStore uses default when given bcrypt-cost is invalid
|
||||
func TestNewAuthStoreBcryptCost(t *testing.T) {
|
||||
b, _ := betesting.NewDefaultTmpBackend(t)
|
||||
defer betesting.Close(t, b)
|
||||
@ -124,6 +125,13 @@ func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testin
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The UserAdd function cannot generate old etcd version user data (user's option is nil)
|
||||
// add special users through the underlying interface
|
||||
err = addUserWithNoOption(as)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tearDown := func(_ *testing.T) {
|
||||
b.Close()
|
||||
as.Close()
|
||||
@ -131,6 +139,14 @@ func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testin
|
||||
return as, tearDown
|
||||
}
|
||||
|
||||
func addUserWithNoOption(as *authStore) error {
|
||||
_, err := as.UserAdd(&pb.AuthUserAddRequest{Name: "foo-no-user-options", Password: "bar"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableAuthAndCreateRoot(as *authStore) error {
|
||||
_, err := as.UserAdd(&pb.AuthUserAddRequest{Name: "root", HashedPassword: encodePassword("root"), Options: &authpb.UserAddOptions{NoPassword: false}})
|
||||
if err != nil {
|
||||
@ -189,6 +205,30 @@ func TestRecover(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoverWithEmptyRangePermCache(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer as.Close()
|
||||
defer tearDown(t)
|
||||
|
||||
as.enabled = false
|
||||
as.rangePermCache = map[string]*unifiedRangePermissions{}
|
||||
as.Recover(as.be)
|
||||
|
||||
if !as.IsAuthEnabled() {
|
||||
t.Fatalf("expected auth enabled got disabled")
|
||||
}
|
||||
|
||||
if len(as.rangePermCache) != 3 {
|
||||
t.Fatalf("rangePermCache should have permission information for 3 users (\"root\" and \"foo\",\"foo-no-user-options\"), but has %d information", len(as.rangePermCache))
|
||||
}
|
||||
if _, ok := as.rangePermCache["root"]; !ok {
|
||||
t.Fatal("user \"root\" should be created by setupAuthStore() but doesn't exist in rangePermCache")
|
||||
}
|
||||
if _, ok := as.rangePermCache["foo"]; !ok {
|
||||
t.Fatal("user \"foo\" should be created by setupAuthStore() but doesn't exist in rangePermCache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckPassword(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
@ -310,6 +350,12 @@ func TestUserChangePassword(t *testing.T) {
|
||||
if err != ErrUserNotFound {
|
||||
t.Fatalf("expected %v, got %v", ErrUserNotFound, err)
|
||||
}
|
||||
|
||||
// change a user(user option is nil) password
|
||||
_, err = as.UserChangePassword(&pb.AuthUserChangePasswordRequest{Name: "foo-no-user-options", HashedPassword: encodePassword("bar")})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleAdd(t *testing.T) {
|
||||
@ -517,6 +563,144 @@ func TestRoleGrantPermission(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleGrantInvalidPermission(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
|
||||
_, err := as.RoleAdd(&pb.AuthRoleAddRequest{Name: "role-test-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
perm *authpb.Permission
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "valid range",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: []byte("RangeEnd"),
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid range: nil key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: nil,
|
||||
RangeEnd: []byte("RangeEnd"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: nil,
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("Keys"),
|
||||
RangeEnd: []byte{},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid range: empty (Key == RangeEnd)",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("a"),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: empty (Key > RangeEnd)",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("b"),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte("a"),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte(""),
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "invalid range: length of key is 0",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte(""),
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: ErrInvalidAuthMgmt,
|
||||
},
|
||||
{
|
||||
name: "valid range: single key permission for []byte{0x00}",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte{0x00},
|
||||
RangeEnd: []byte(""),
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: \"a\" or larger keys",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte("a"),
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid range: the entire keys",
|
||||
perm: &authpb.Permission{
|
||||
PermType: authpb.WRITE,
|
||||
Key: []byte{0x00},
|
||||
RangeEnd: []byte{0x00},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err = as.RoleGrantPermission(&pb.AuthRoleGrantPermissionRequest{
|
||||
Name: "role-test-1",
|
||||
Perm: tt.perm,
|
||||
})
|
||||
|
||||
if !errors.Is(err, tt.want) {
|
||||
t.Errorf("#%d: result=%t, want=%t", i, err, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleRevokePermission(t *testing.T) {
|
||||
as, tearDown := setupAuthStore(t)
|
||||
defer tearDown(t)
|
||||
@ -742,7 +926,7 @@ func TestIsAuthEnabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthRevisionRace ensures that access to authStore.revision is thread-safe.
|
||||
// TestAuthInfoFromCtxRace ensures that access to authStore.revision is thread-safe.
|
||||
func TestAuthInfoFromCtxRace(t *testing.T) {
|
||||
b, _ := betesting.NewDefaultTmpBackend(t)
|
||||
defer betesting.Close(t, b)
|
||||
|
@ -15,6 +15,7 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
@ -204,12 +205,12 @@ type Config struct {
|
||||
// streams that each client can open at a time.
|
||||
MaxConcurrentStreams uint32 `json:"max-concurrent-streams"`
|
||||
|
||||
LPUrls, LCUrls []url.URL
|
||||
APUrls, ACUrls []url.URL
|
||||
ClientTLSInfo transport.TLSInfo
|
||||
ClientAutoTLS bool
|
||||
PeerTLSInfo transport.TLSInfo
|
||||
PeerAutoTLS bool
|
||||
ListenPeerUrls, ListenClientUrls, ListenClientHttpUrls []url.URL
|
||||
AdvertisePeerUrls, AdvertiseClientUrls []url.URL
|
||||
ClientTLSInfo transport.TLSInfo
|
||||
ClientAutoTLS bool
|
||||
PeerTLSInfo transport.TLSInfo
|
||||
PeerAutoTLS bool
|
||||
// SelfSignedCertValidity specifies the validity period of the client and peer certificates
|
||||
// that are automatically generated by etcd when you specify ClientAutoTLS and PeerAutoTLS,
|
||||
// the unit is year, and the default is 1
|
||||
@ -220,6 +221,11 @@ type Config struct {
|
||||
// Note that cipher suites are prioritized in the given order.
|
||||
CipherSuites []string `json:"cipher-suites"`
|
||||
|
||||
// TlsMinVersion is the minimum accepted TLS version between client/server and peers.
|
||||
TlsMinVersion string `json:"tls-min-version"`
|
||||
// TlsMaxVersion is the maximum accepted TLS version between client/server and peers.
|
||||
TlsMaxVersion string `json:"tls-max-version"`
|
||||
|
||||
ClusterState string `json:"initial-cluster-state"`
|
||||
DNSCluster string `json:"discovery-srv"`
|
||||
DNSClusterServiceName string `json:"discovery-srv-name"`
|
||||
@ -417,10 +423,11 @@ type configYAML struct {
|
||||
|
||||
// configJSON has file options that are translated into Config options
|
||||
type configJSON struct {
|
||||
LPUrlsJSON string `json:"listen-peer-urls"`
|
||||
LCUrlsJSON string `json:"listen-client-urls"`
|
||||
APUrlsJSON string `json:"initial-advertise-peer-urls"`
|
||||
ACUrlsJSON string `json:"advertise-client-urls"`
|
||||
ListenPeerUrls string `json:"listen-peer-urls"`
|
||||
ListenClientUrls string `json:"listen-client-urls"`
|
||||
ListenClientHttpUrls string `json:"listen-client-http-urls"`
|
||||
AdvertisePeerUrls string `json:"initial-advertise-peer-urls"`
|
||||
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
||||
|
||||
CORSJSON string `json:"cors"`
|
||||
HostWhitelistJSON string `json:"host-whitelist"`
|
||||
@ -469,10 +476,10 @@ func NewConfig() *Config {
|
||||
ElectionMs: 1000,
|
||||
InitialElectionTickAdvance: true,
|
||||
|
||||
LPUrls: []url.URL{*lpurl},
|
||||
LCUrls: []url.URL{*lcurl},
|
||||
APUrls: []url.URL{*apurl},
|
||||
ACUrls: []url.URL{*acurl},
|
||||
ListenPeerUrls: []url.URL{*lpurl},
|
||||
ListenClientUrls: []url.URL{*lcurl},
|
||||
AdvertisePeerUrls: []url.URL{*apurl},
|
||||
AdvertiseClientUrls: []url.URL{*acurl},
|
||||
|
||||
ClusterState: ClusterStateFlagNew,
|
||||
InitialClusterToken: "etcd-cluster",
|
||||
@ -533,40 +540,49 @@ func (cfg *configYAML) configFromFile(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.LPUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.LPUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenPeerUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenPeerUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.LPUrls = []url.URL(u)
|
||||
cfg.Config.ListenPeerUrls = u
|
||||
}
|
||||
|
||||
if cfg.LCUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.LCUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenClientUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.LCUrls = []url.URL(u)
|
||||
cfg.Config.ListenClientUrls = u
|
||||
}
|
||||
|
||||
if cfg.APUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.APUrlsJSON, ","))
|
||||
if cfg.configJSON.ListenClientHttpUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.ListenClientHttpUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up listen-client-http-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.Config.ListenClientHttpUrls = u
|
||||
}
|
||||
|
||||
if cfg.configJSON.AdvertisePeerUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertisePeerUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up initial-advertise-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.APUrls = []url.URL(u)
|
||||
cfg.Config.AdvertisePeerUrls = u
|
||||
}
|
||||
|
||||
if cfg.ACUrlsJSON != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.ACUrlsJSON, ","))
|
||||
if cfg.configJSON.AdvertiseClientUrls != "" {
|
||||
u, err := types.NewURLs(strings.Split(cfg.configJSON.AdvertiseClientUrls, ","))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unexpected error setting up advertise-peer-urls: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.ACUrls = []url.URL(u)
|
||||
cfg.Config.AdvertiseClientUrls = u
|
||||
}
|
||||
|
||||
if cfg.ListenMetricsUrlsJSON != "" {
|
||||
@ -619,39 +635,52 @@ func updateCipherSuites(tls *transport.TLSInfo, ss []string) error {
|
||||
return fmt.Errorf("TLSInfo.CipherSuites is already specified (given %v)", ss)
|
||||
}
|
||||
if len(ss) > 0 {
|
||||
cs := make([]uint16, len(ss))
|
||||
for i, s := range ss {
|
||||
var ok bool
|
||||
cs[i], ok = tlsutil.GetCipherSuite(s)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected TLS cipher suite %q", s)
|
||||
}
|
||||
cs, err := tlsutil.GetCipherSuites(ss)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tls.CipherSuites = cs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateMinMaxVersions(info *transport.TLSInfo, min, max string) {
|
||||
// Validate() has been called to check the user input, so it should never fail.
|
||||
var err error
|
||||
if info.MinVersion, err = tlsutil.GetTLSVersion(min); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if info.MaxVersion, err = tlsutil.GetTLSVersion(max); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate ensures that '*embed.Config' fields are properly configured.
|
||||
func (cfg *Config) Validate() error {
|
||||
if err := cfg.setupLogging(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.LPUrls); err != nil {
|
||||
if err := checkBindURLs(cfg.ListenPeerUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.LCUrls); err != nil {
|
||||
if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cfg.ListenClientHttpUrls) == 0 {
|
||||
cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
|
||||
}
|
||||
if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkHostURLs(cfg.APUrls); err != nil {
|
||||
addrs := cfg.getAPURLs()
|
||||
if err := checkHostURLs(cfg.AdvertisePeerUrls); err != nil {
|
||||
addrs := cfg.getAdvertisePeerUrls()
|
||||
return fmt.Errorf(`--initial-advertise-peer-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
|
||||
}
|
||||
if err := checkHostURLs(cfg.ACUrls); err != nil {
|
||||
addrs := cfg.getACURLs()
|
||||
if err := checkHostURLs(cfg.AdvertiseClientUrls); err != nil {
|
||||
addrs := cfg.getAdvertiseClientUrls()
|
||||
return fmt.Errorf(`--advertise-client-urls %q must be "host:port" (%v)`, strings.Join(addrs, ","), err)
|
||||
}
|
||||
// Check if conflicting flags are passed.
|
||||
@ -684,7 +713,7 @@ func (cfg *Config) Validate() error {
|
||||
}
|
||||
|
||||
// check this last since proxying in etcdmain may make this OK
|
||||
if cfg.LCUrls != nil && cfg.ACUrls == nil {
|
||||
if cfg.ListenClientUrls != nil && cfg.AdvertiseClientUrls == nil {
|
||||
return ErrUnsetAdvertiseClientURLsFlag
|
||||
}
|
||||
|
||||
@ -707,6 +736,25 @@ func (cfg *Config) Validate() error {
|
||||
return fmt.Errorf("--experimental-compact-hash-check-time must be >0 (set to %v)", cfg.ExperimentalCompactHashCheckTime)
|
||||
}
|
||||
|
||||
minVersion, err := tlsutil.GetTLSVersion(cfg.TlsMinVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
maxVersion, err := tlsutil.GetTLSVersion(cfg.TlsMaxVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// maxVersion == 0 means that Go selects the highest available version.
|
||||
if maxVersion != 0 && minVersion > maxVersion {
|
||||
return fmt.Errorf("min version (%s) is greater than max version (%s)", cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
}
|
||||
|
||||
// Check if user attempted to configure ciphers for TLS1.3 only: Go does not support that currently.
|
||||
if minVersion == tls.VersionTLS13 && len(cfg.CipherSuites) > 0 {
|
||||
return fmt.Errorf("cipher suites cannot be configured when only TLS1.3 is enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -718,7 +766,7 @@ func (cfg *Config) PeerURLsMapAndToken(which string) (urlsmap types.URLsMap, tok
|
||||
urlsmap = types.URLsMap{}
|
||||
// If using discovery, generate a temporary cluster based on
|
||||
// self's advertised peer URLs
|
||||
urlsmap[cfg.Name] = cfg.APUrls
|
||||
urlsmap[cfg.Name] = cfg.AdvertisePeerUrls
|
||||
token = cfg.Durl
|
||||
|
||||
case cfg.DNSCluster != "":
|
||||
@ -772,7 +820,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
||||
|
||||
// Use both etcd-server-ssl and etcd-server for discovery.
|
||||
// Combine the results if both are available.
|
||||
clusterStrs, cerr = getCluster("https", "etcd-server-ssl"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.APUrls)
|
||||
clusterStrs, cerr = getCluster("https", "etcd-server-ssl"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
|
||||
if cerr != nil {
|
||||
clusterStrs = make([]string, 0)
|
||||
}
|
||||
@ -782,12 +830,12 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
||||
zap.String("service-name", "etcd-server-ssl"+serviceNameSuffix),
|
||||
zap.String("server-name", cfg.Name),
|
||||
zap.String("discovery-srv", cfg.DNSCluster),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAPURLs()),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("found-cluster", clusterStrs),
|
||||
zap.Error(cerr),
|
||||
)
|
||||
|
||||
defaultHTTPClusterStrs, httpCerr := getCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.APUrls)
|
||||
defaultHTTPClusterStrs, httpCerr := getCluster("http", "etcd-server"+serviceNameSuffix, cfg.Name, cfg.DNSCluster, cfg.AdvertisePeerUrls)
|
||||
if httpCerr == nil {
|
||||
clusterStrs = append(clusterStrs, defaultHTTPClusterStrs...)
|
||||
}
|
||||
@ -797,7 +845,7 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
||||
zap.String("service-name", "etcd-server"+serviceNameSuffix),
|
||||
zap.String("server-name", cfg.Name),
|
||||
zap.String("discovery-srv", cfg.DNSCluster),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAPURLs()),
|
||||
zap.Strings("advertise-peer-urls", cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("found-cluster", clusterStrs),
|
||||
zap.Error(httpCerr),
|
||||
)
|
||||
@ -806,15 +854,15 @@ func (cfg *Config) GetDNSClusterNames() ([]string, error) {
|
||||
}
|
||||
|
||||
func (cfg Config) InitialClusterFromName(name string) (ret string) {
|
||||
if len(cfg.APUrls) == 0 {
|
||||
if len(cfg.AdvertisePeerUrls) == 0 {
|
||||
return ""
|
||||
}
|
||||
n := name
|
||||
if name == "" {
|
||||
n = DefaultName
|
||||
}
|
||||
for i := range cfg.APUrls {
|
||||
ret = ret + "," + n + "=" + cfg.APUrls[i].String()
|
||||
for i := range cfg.AdvertisePeerUrls {
|
||||
ret = ret + "," + n + "=" + cfg.AdvertisePeerUrls[i].String()
|
||||
}
|
||||
return ret[1:]
|
||||
}
|
||||
@ -830,11 +878,11 @@ func (cfg Config) V2DeprecationEffective() config.V2DeprecationEnum {
|
||||
}
|
||||
|
||||
func (cfg Config) defaultPeerHost() bool {
|
||||
return len(cfg.APUrls) == 1 && cfg.APUrls[0].String() == DefaultInitialAdvertisePeerURLs
|
||||
return len(cfg.AdvertisePeerUrls) == 1 && cfg.AdvertisePeerUrls[0].String() == DefaultInitialAdvertisePeerURLs
|
||||
}
|
||||
|
||||
func (cfg Config) defaultClientHost() bool {
|
||||
return len(cfg.ACUrls) == 1 && cfg.ACUrls[0].String() == DefaultAdvertiseClientURLs
|
||||
return len(cfg.AdvertiseClientUrls) == 1 && cfg.AdvertiseClientUrls[0].String() == DefaultAdvertiseClientURLs
|
||||
}
|
||||
|
||||
func (cfg *Config) ClientSelfCert() (err error) {
|
||||
@ -845,9 +893,12 @@ func (cfg *Config) ClientSelfCert() (err error) {
|
||||
cfg.logger.Warn("ignoring client auto TLS since certs given")
|
||||
return nil
|
||||
}
|
||||
chosts := make([]string, len(cfg.LCUrls))
|
||||
for i, u := range cfg.LCUrls {
|
||||
chosts[i] = u.Host
|
||||
chosts := make([]string, 0, len(cfg.ListenClientUrls)+len(cfg.ListenClientHttpUrls))
|
||||
for _, u := range cfg.ListenClientUrls {
|
||||
chosts = append(chosts, u.Host)
|
||||
}
|
||||
for _, u := range cfg.ListenClientHttpUrls {
|
||||
chosts = append(chosts, u.Host)
|
||||
}
|
||||
cfg.ClientTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "client"), chosts, cfg.SelfSignedCertValidity)
|
||||
if err != nil {
|
||||
@ -864,8 +915,8 @@ func (cfg *Config) PeerSelfCert() (err error) {
|
||||
cfg.logger.Warn("ignoring peer auto TLS since certs given")
|
||||
return nil
|
||||
}
|
||||
phosts := make([]string, len(cfg.LPUrls))
|
||||
for i, u := range cfg.LPUrls {
|
||||
phosts := make([]string, len(cfg.ListenPeerUrls))
|
||||
for i, u := range cfg.ListenPeerUrls {
|
||||
phosts[i] = u.Host
|
||||
}
|
||||
cfg.PeerTLSInfo, err = transport.SelfCert(cfg.logger, filepath.Join(cfg.Dir, "fixtures", "peer"), phosts, cfg.SelfSignedCertValidity)
|
||||
@ -893,9 +944,9 @@ func (cfg *Config) UpdateDefaultClusterFromName(defaultInitialCluster string) (s
|
||||
}
|
||||
|
||||
used := false
|
||||
pip, pport := cfg.LPUrls[0].Hostname(), cfg.LPUrls[0].Port()
|
||||
pip, pport := cfg.ListenPeerUrls[0].Hostname(), cfg.ListenPeerUrls[0].Port()
|
||||
if cfg.defaultPeerHost() && pip == "0.0.0.0" {
|
||||
cfg.APUrls[0] = url.URL{Scheme: cfg.APUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, pport)}
|
||||
cfg.AdvertisePeerUrls[0] = url.URL{Scheme: cfg.AdvertisePeerUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, pport)}
|
||||
used = true
|
||||
}
|
||||
// update 'initial-cluster' when only the name is specified (e.g. 'etcd --name=abc')
|
||||
@ -903,9 +954,9 @@ func (cfg *Config) UpdateDefaultClusterFromName(defaultInitialCluster string) (s
|
||||
cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
|
||||
}
|
||||
|
||||
cip, cport := cfg.LCUrls[0].Hostname(), cfg.LCUrls[0].Port()
|
||||
cip, cport := cfg.ListenClientUrls[0].Hostname(), cfg.ListenClientUrls[0].Port()
|
||||
if cfg.defaultClientHost() && cip == "0.0.0.0" {
|
||||
cfg.ACUrls[0] = url.URL{Scheme: cfg.ACUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, cport)}
|
||||
cfg.AdvertiseClientUrls[0] = url.URL{Scheme: cfg.AdvertiseClientUrls[0].Scheme, Host: fmt.Sprintf("%s:%s", defaultHostname, cport)}
|
||||
used = true
|
||||
}
|
||||
dhost := defaultHostname
|
||||
@ -950,34 +1001,42 @@ func checkHostURLs(urls []url.URL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) getAPURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.APUrls))
|
||||
for i := range cfg.APUrls {
|
||||
ss[i] = cfg.APUrls[i].String()
|
||||
func (cfg *Config) getAdvertisePeerUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.AdvertisePeerUrls))
|
||||
for i := range cfg.AdvertisePeerUrls {
|
||||
ss[i] = cfg.AdvertisePeerUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getLPURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.LPUrls))
|
||||
for i := range cfg.LPUrls {
|
||||
ss[i] = cfg.LPUrls[i].String()
|
||||
func (cfg *Config) getListenPeerUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenPeerUrls))
|
||||
for i := range cfg.ListenPeerUrls {
|
||||
ss[i] = cfg.ListenPeerUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getACURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.ACUrls))
|
||||
for i := range cfg.ACUrls {
|
||||
ss[i] = cfg.ACUrls[i].String()
|
||||
func (cfg *Config) getAdvertiseClientUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.AdvertiseClientUrls))
|
||||
for i := range cfg.AdvertiseClientUrls {
|
||||
ss[i] = cfg.AdvertiseClientUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getLCURLs() (ss []string) {
|
||||
ss = make([]string, len(cfg.LCUrls))
|
||||
for i := range cfg.LCUrls {
|
||||
ss[i] = cfg.LCUrls[i].String()
|
||||
func (cfg *Config) getListenClientUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenClientUrls))
|
||||
for i := range cfg.ListenClientUrls {
|
||||
ss[i] = cfg.ListenClientUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func (cfg *Config) getListenClientHttpUrls() (ss []string) {
|
||||
ss = make([]string, len(cfg.ListenClientHttpUrls))
|
||||
for i := range cfg.ListenClientHttpUrls {
|
||||
ss[i] = cfg.ListenClientHttpUrls[i].String()
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
package embed
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -24,6 +25,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.etcd.io/etcd/client/pkg/v3/srv"
|
||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||
"go.etcd.io/etcd/client/pkg/v3/types"
|
||||
@ -84,12 +86,12 @@ func TestConfigFileOtherFields(t *testing.T) {
|
||||
func TestUpdateDefaultClusterFromName(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
defaultInitialCluster := cfg.InitialCluster
|
||||
oldscheme := cfg.APUrls[0].Scheme
|
||||
origpeer := cfg.APUrls[0].String()
|
||||
origadvc := cfg.ACUrls[0].String()
|
||||
oldscheme := cfg.AdvertisePeerUrls[0].Scheme
|
||||
origpeer := cfg.AdvertisePeerUrls[0].String()
|
||||
origadvc := cfg.AdvertiseClientUrls[0].String()
|
||||
|
||||
cfg.Name = "abc"
|
||||
lpport := cfg.LPUrls[0].Port()
|
||||
lpport := cfg.ListenPeerUrls[0].Port()
|
||||
|
||||
// in case of 'etcd --name=abc'
|
||||
exp := fmt.Sprintf("%s=%s://localhost:%s", cfg.Name, oldscheme, lpport)
|
||||
@ -98,12 +100,12 @@ func TestUpdateDefaultClusterFromName(t *testing.T) {
|
||||
t.Fatalf("initial-cluster expected %q, got %q", exp, cfg.InitialCluster)
|
||||
}
|
||||
// advertise peer URL should not be affected
|
||||
if origpeer != cfg.APUrls[0].String() {
|
||||
t.Fatalf("advertise peer url expected %q, got %q", origadvc, cfg.APUrls[0].String())
|
||||
if origpeer != cfg.AdvertisePeerUrls[0].String() {
|
||||
t.Fatalf("advertise peer url expected %q, got %q", origadvc, cfg.AdvertisePeerUrls[0].String())
|
||||
}
|
||||
// advertise client URL should not be affected
|
||||
if origadvc != cfg.ACUrls[0].String() {
|
||||
t.Fatalf("advertise client url expected %q, got %q", origadvc, cfg.ACUrls[0].String())
|
||||
if origadvc != cfg.AdvertiseClientUrls[0].String() {
|
||||
t.Fatalf("advertise client url expected %q, got %q", origadvc, cfg.AdvertiseClientUrls[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,17 +118,17 @@ func TestUpdateDefaultClusterFromNameOverwrite(t *testing.T) {
|
||||
|
||||
cfg := NewConfig()
|
||||
defaultInitialCluster := cfg.InitialCluster
|
||||
oldscheme := cfg.APUrls[0].Scheme
|
||||
origadvc := cfg.ACUrls[0].String()
|
||||
oldscheme := cfg.AdvertisePeerUrls[0].Scheme
|
||||
origadvc := cfg.AdvertiseClientUrls[0].String()
|
||||
|
||||
cfg.Name = "abc"
|
||||
lpport := cfg.LPUrls[0].Port()
|
||||
cfg.LPUrls[0] = url.URL{Scheme: cfg.LPUrls[0].Scheme, Host: fmt.Sprintf("0.0.0.0:%s", lpport)}
|
||||
lpport := cfg.ListenPeerUrls[0].Port()
|
||||
cfg.ListenPeerUrls[0] = url.URL{Scheme: cfg.ListenPeerUrls[0].Scheme, Host: fmt.Sprintf("0.0.0.0:%s", lpport)}
|
||||
dhost, _ := cfg.UpdateDefaultClusterFromName(defaultInitialCluster)
|
||||
if dhost != defaultHostname {
|
||||
t.Fatalf("expected default host %q, got %q", defaultHostname, dhost)
|
||||
}
|
||||
aphost, apport := cfg.APUrls[0].Hostname(), cfg.APUrls[0].Port()
|
||||
aphost, apport := cfg.AdvertisePeerUrls[0].Hostname(), cfg.AdvertisePeerUrls[0].Port()
|
||||
if apport != lpport {
|
||||
t.Fatalf("advertise peer url got different port %s, expected %s", apport, lpport)
|
||||
}
|
||||
@ -139,8 +141,8 @@ func TestUpdateDefaultClusterFromNameOverwrite(t *testing.T) {
|
||||
}
|
||||
|
||||
// advertise client URL should not be affected
|
||||
if origadvc != cfg.ACUrls[0].String() {
|
||||
t.Fatalf("advertise-client-url expected %q, got %q", origadvc, cfg.ACUrls[0].String())
|
||||
if origadvc != cfg.AdvertiseClientUrls[0].String() {
|
||||
t.Fatalf("advertise-client-url expected %q, got %q", origadvc, cfg.AdvertiseClientUrls[0].String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +276,7 @@ func TestPeerURLsMapAndTokenFromSRV(t *testing.T) {
|
||||
cfg.InitialCluster = ""
|
||||
cfg.InitialClusterToken = ""
|
||||
cfg.DNSCluster = "example.com"
|
||||
cfg.APUrls = types.MustNewURLs(tt.apurls)
|
||||
cfg.AdvertisePeerUrls = types.MustNewURLs(tt.apurls)
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
t.Errorf("#%d: failed to validate test Config: %v", i, err)
|
||||
@ -422,3 +424,80 @@ func TestLogRotation(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSVersionMinMax(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
givenTLSMinVersion string
|
||||
givenTLSMaxVersion string
|
||||
givenCipherSuites []string
|
||||
expectError bool
|
||||
expectedMinTLSVersion uint16
|
||||
expectedMaxTLSVersion uint16
|
||||
}{
|
||||
{
|
||||
name: "Minimum TLS version is set",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
expectedMinTLSVersion: tls.VersionTLS13,
|
||||
expectedMaxTLSVersion: 0,
|
||||
},
|
||||
{
|
||||
name: "Maximum TLS version is set",
|
||||
givenTLSMaxVersion: "TLS1.2",
|
||||
expectedMinTLSVersion: 0,
|
||||
expectedMaxTLSVersion: tls.VersionTLS12,
|
||||
},
|
||||
{
|
||||
name: "Minimum and Maximum TLS versions are set",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenTLSMaxVersion: "TLS1.3",
|
||||
expectedMinTLSVersion: tls.VersionTLS13,
|
||||
expectedMaxTLSVersion: tls.VersionTLS13,
|
||||
},
|
||||
{
|
||||
name: "Minimum and Maximum TLS versions are set in reverse order",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenTLSMaxVersion: "TLS1.2",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid minimum TLS version",
|
||||
givenTLSMinVersion: "invalid version",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid maximum TLS version",
|
||||
givenTLSMaxVersion: "invalid version",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Cipher suites configured for TLS 1.3",
|
||||
givenTLSMinVersion: "TLS1.3",
|
||||
givenCipherSuites: []string{"TLS_AES_128_GCM_SHA256"},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := NewConfig()
|
||||
cfg.TlsMinVersion = tt.givenTLSMinVersion
|
||||
cfg.TlsMaxVersion = tt.givenTLSMaxVersion
|
||||
cfg.CipherSuites = tt.givenCipherSuites
|
||||
|
||||
err := cfg.Validate()
|
||||
if err != nil {
|
||||
assert.True(t, tt.expectError, "Validate() returned error while expecting success: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
assert.Equal(t, tt.expectedMinTLSVersion, cfg.PeerTLSInfo.MinVersion)
|
||||
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.PeerTLSInfo.MaxVersion)
|
||||
assert.Equal(t, tt.expectedMinTLSVersion, cfg.ClientTLSInfo.MinVersion)
|
||||
assert.Equal(t, tt.expectedMaxTLSVersion, cfg.ClientTLSInfo.MaxVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
defaultLog "log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -32,6 +33,7 @@ import (
|
||||
"go.etcd.io/etcd/api/v3/version"
|
||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||
"go.etcd.io/etcd/client/pkg/v3/types"
|
||||
"go.etcd.io/etcd/client/v3/credentials"
|
||||
"go.etcd.io/etcd/pkg/v3/debugutil"
|
||||
runtimeutil "go.etcd.io/etcd/pkg/v3/runtime"
|
||||
"go.etcd.io/etcd/server/v3/config"
|
||||
@ -48,6 +50,7 @@ import (
|
||||
"github.com/soheilhy/cmux"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
@ -123,7 +126,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
||||
}
|
||||
e.cfg.logger.Info(
|
||||
"configuring peer listeners",
|
||||
zap.Strings("listen-peer-urls", e.cfg.getLPURLs()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getListenPeerUrls()),
|
||||
)
|
||||
if e.Peers, err = configurePeerListeners(cfg); err != nil {
|
||||
return e, err
|
||||
@ -131,7 +134,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
||||
|
||||
e.cfg.logger.Info(
|
||||
"configuring client listeners",
|
||||
zap.Strings("listen-client-urls", e.cfg.getLCURLs()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getListenClientUrls()),
|
||||
)
|
||||
if e.sctxs, err = configureClientListeners(cfg); err != nil {
|
||||
return e, err
|
||||
@ -167,8 +170,8 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
||||
|
||||
srvcfg := config.ServerConfig{
|
||||
Name: cfg.Name,
|
||||
ClientURLs: cfg.ACUrls,
|
||||
PeerURLs: cfg.APUrls,
|
||||
ClientURLs: cfg.AdvertiseClientUrls,
|
||||
PeerURLs: cfg.AdvertisePeerUrls,
|
||||
DataDir: cfg.Dir,
|
||||
DedicatedWALDir: cfg.WalDir,
|
||||
SnapshotCount: cfg.SnapshotCount,
|
||||
@ -275,10 +278,10 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
|
||||
e.cfg.logger.Info(
|
||||
"now serving peer/client/metrics",
|
||||
zap.String("local-member-id", e.Server.ID().String()),
|
||||
zap.Strings("initial-advertise-peer-urls", e.cfg.getAPURLs()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getLPURLs()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getACURLs()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getLCURLs()),
|
||||
zap.Strings("initial-advertise-peer-urls", e.cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("listen-peer-urls", e.cfg.getListenPeerUrls()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getAdvertiseClientUrls()),
|
||||
zap.Strings("listen-client-urls", e.cfg.getListenClientUrls()),
|
||||
zap.Strings("listen-metrics-urls", e.cfg.getMetricsURLs()),
|
||||
)
|
||||
serving = true
|
||||
@ -323,11 +326,13 @@ func print(lg *zap.Logger, ec Config, sc config.ServerConfig, memberInitialized
|
||||
zap.String("election-timeout", fmt.Sprintf("%v", time.Duration(sc.ElectionTicks*int(sc.TickMs))*time.Millisecond)),
|
||||
zap.Bool("initial-election-tick-advance", sc.InitialElectionTickAdvance),
|
||||
zap.Uint64("snapshot-count", sc.SnapshotCount),
|
||||
zap.Uint("max-wals", sc.MaxWALFiles),
|
||||
zap.Uint("max-snapshots", sc.MaxSnapFiles),
|
||||
zap.Uint64("snapshot-catchup-entries", sc.SnapshotCatchUpEntries),
|
||||
zap.Strings("initial-advertise-peer-urls", ec.getAPURLs()),
|
||||
zap.Strings("listen-peer-urls", ec.getLPURLs()),
|
||||
zap.Strings("advertise-client-urls", ec.getACURLs()),
|
||||
zap.Strings("listen-client-urls", ec.getLCURLs()),
|
||||
zap.Strings("initial-advertise-peer-urls", ec.getAdvertisePeerUrls()),
|
||||
zap.Strings("listen-peer-urls", ec.getListenPeerUrls()),
|
||||
zap.Strings("advertise-client-urls", ec.getAdvertiseClientUrls()),
|
||||
zap.Strings("listen-client-urls", ec.getListenClientUrls()),
|
||||
zap.Strings("listen-metrics-urls", ec.getMetricsURLs()),
|
||||
zap.Strings("cors", cors),
|
||||
zap.Strings("host-whitelist", hss),
|
||||
@ -364,8 +369,8 @@ func (e *Etcd) Close() {
|
||||
fields := []zap.Field{
|
||||
zap.String("name", e.cfg.Name),
|
||||
zap.String("data-dir", e.cfg.Dir),
|
||||
zap.Strings("advertise-peer-urls", e.cfg.getAPURLs()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getACURLs()),
|
||||
zap.Strings("advertise-peer-urls", e.cfg.getAdvertisePeerUrls()),
|
||||
zap.Strings("advertise-client-urls", e.cfg.getAdvertiseClientUrls()),
|
||||
}
|
||||
lg := e.GetLogger()
|
||||
lg.Info("closing etcd server", fields...)
|
||||
@ -435,11 +440,16 @@ func (e *Etcd) Close() {
|
||||
|
||||
func stopServers(ctx context.Context, ss *servers) {
|
||||
// first, close the http.Server
|
||||
ss.http.Shutdown(ctx)
|
||||
// do not grpc.Server.GracefulStop with TLS enabled etcd server
|
||||
if ss.http != nil {
|
||||
ss.http.Shutdown(ctx)
|
||||
}
|
||||
if ss.grpc == nil {
|
||||
return
|
||||
}
|
||||
// do not grpc.Server.GracefulStop when grpc runs under http server
|
||||
// See https://github.com/grpc/grpc-go/issues/1384#issuecomment-317124531
|
||||
// and https://github.com/etcd-io/etcd/issues/8916
|
||||
if ss.secure {
|
||||
if ss.secure && ss.http != nil {
|
||||
ss.grpc.Stop()
|
||||
return
|
||||
}
|
||||
@ -478,6 +488,9 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
||||
if err = cfg.PeerSelfCert(); err != nil {
|
||||
cfg.logger.Fatal("failed to get peer self-signed certs", zap.Error(err))
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.PeerTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
if !cfg.PeerTLSInfo.Empty() {
|
||||
cfg.logger.Info(
|
||||
"starting with peer TLS",
|
||||
@ -486,7 +499,7 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
||||
)
|
||||
}
|
||||
|
||||
peers = make([]*peerListener, len(cfg.LPUrls))
|
||||
peers = make([]*peerListener, len(cfg.ListenPeerUrls))
|
||||
defer func() {
|
||||
if err == nil {
|
||||
return
|
||||
@ -495,7 +508,7 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
||||
if peers[i] != nil && peers[i].close != nil {
|
||||
cfg.logger.Warn(
|
||||
"closing peer listener",
|
||||
zap.String("address", cfg.LPUrls[i].String()),
|
||||
zap.String("address", cfg.ListenPeerUrls[i].String()),
|
||||
zap.Error(err),
|
||||
)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
@ -505,7 +518,7 @@ func configurePeerListeners(cfg *Config) (peers []*peerListener, err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
for i, u := range cfg.LPUrls {
|
||||
for i, u := range cfg.ListenPeerUrls {
|
||||
if u.Scheme == "http" {
|
||||
if !cfg.PeerTLSInfo.Empty() {
|
||||
cfg.logger.Warn("scheme is HTTP while key and cert files are present; ignoring key and cert files", zap.String("peer-url", u.String()))
|
||||
@ -598,13 +611,15 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
||||
if err = cfg.ClientSelfCert(); err != nil {
|
||||
cfg.logger.Fatal("failed to get client self-signed certs", zap.Error(err))
|
||||
}
|
||||
|
||||
updateMinMaxVersions(&cfg.ClientTLSInfo, cfg.TlsMinVersion, cfg.TlsMaxVersion)
|
||||
|
||||
if cfg.EnablePprof {
|
||||
cfg.logger.Info("pprof is enabled", zap.String("path", debugutil.HTTPPrefixPProf))
|
||||
}
|
||||
|
||||
sctxs = make(map[string]*serveCtx)
|
||||
for _, u := range cfg.LCUrls {
|
||||
sctx := newServeCtx(cfg.logger)
|
||||
for _, u := range append(cfg.ListenClientUrls, cfg.ListenClientHttpUrls...) {
|
||||
if u.Scheme == "http" || u.Scheme == "unix" {
|
||||
if !cfg.ClientTLSInfo.Empty() {
|
||||
cfg.logger.Warn("scheme is HTTP while key and cert files are present; ignoring key and cert files", zap.String("client-url", u.String()))
|
||||
@ -616,24 +631,41 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
||||
if (u.Scheme == "https" || u.Scheme == "unixs") && cfg.ClientTLSInfo.Empty() {
|
||||
return nil, fmt.Errorf("TLS key/cert (--cert-file, --key-file) must be provided for client url %s with HTTPS scheme", u.String())
|
||||
}
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
addr := u.Host
|
||||
if u.Scheme == "unix" || u.Scheme == "unixs" {
|
||||
network = "unix"
|
||||
addr = u.Host + u.Path
|
||||
for _, u := range cfg.ListenClientUrls {
|
||||
addr, secure, network := resolveUrl(u)
|
||||
sctx := sctxs[addr]
|
||||
if sctx == nil {
|
||||
sctx = newServeCtx(cfg.logger)
|
||||
sctxs[addr] = sctx
|
||||
}
|
||||
sctx.secure = sctx.secure || secure
|
||||
sctx.insecure = sctx.insecure || !secure
|
||||
sctx.scheme = u.Scheme
|
||||
sctx.addr = addr
|
||||
sctx.network = network
|
||||
}
|
||||
for _, u := range cfg.ListenClientHttpUrls {
|
||||
addr, secure, network := resolveUrl(u)
|
||||
|
||||
sctx.secure = u.Scheme == "https" || u.Scheme == "unixs"
|
||||
sctx.insecure = !sctx.secure
|
||||
if oldctx := sctxs[addr]; oldctx != nil {
|
||||
oldctx.secure = oldctx.secure || sctx.secure
|
||||
oldctx.insecure = oldctx.insecure || sctx.insecure
|
||||
continue
|
||||
sctx := sctxs[addr]
|
||||
if sctx == nil {
|
||||
sctx = newServeCtx(cfg.logger)
|
||||
sctxs[addr] = sctx
|
||||
} else if !sctx.httpOnly {
|
||||
return nil, fmt.Errorf("cannot bind both --client-listen-urls and --client-listen-http-urls on the same url %s", u.String())
|
||||
}
|
||||
sctx.secure = sctx.secure || secure
|
||||
sctx.insecure = sctx.insecure || !secure
|
||||
sctx.scheme = u.Scheme
|
||||
sctx.addr = addr
|
||||
sctx.network = network
|
||||
sctx.httpOnly = true
|
||||
}
|
||||
|
||||
if sctx.l, err = transport.NewListenerWithOpts(addr, u.Scheme,
|
||||
for _, sctx := range sctxs {
|
||||
if sctx.l, err = transport.NewListenerWithOpts(sctx.addr, sctx.scheme,
|
||||
transport.WithSocketOpts(&cfg.SocketOpts),
|
||||
transport.WithSkipTLSInfoCheck(true),
|
||||
); err != nil {
|
||||
@ -641,7 +673,6 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
||||
}
|
||||
// net.Listener will rewrite ipv4 0.0.0.0 to ipv6 [::], breaking
|
||||
// hosts that disable ipv6. So, use the address given by the user.
|
||||
sctx.addr = addr
|
||||
|
||||
if fdLimit, fderr := runtimeutil.FDLimit(); fderr == nil {
|
||||
if fdLimit <= reservedInternalFDNum {
|
||||
@ -654,17 +685,17 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
||||
sctx.l = transport.LimitListener(sctx.l, int(fdLimit-reservedInternalFDNum))
|
||||
}
|
||||
|
||||
defer func(u url.URL) {
|
||||
if err == nil {
|
||||
defer func(sctx *serveCtx) {
|
||||
if err == nil || sctx.l == nil {
|
||||
return
|
||||
}
|
||||
sctx.l.Close()
|
||||
cfg.logger.Warn(
|
||||
"closing peer listener",
|
||||
zap.String("address", u.Host),
|
||||
zap.String("address", sctx.addr),
|
||||
zap.Error(err),
|
||||
)
|
||||
}(u)
|
||||
}(sctx)
|
||||
for k := range cfg.UserHandlers {
|
||||
sctx.userHandlers[k] = cfg.UserHandlers[k]
|
||||
}
|
||||
@ -675,11 +706,21 @@ func configureClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err erro
|
||||
if cfg.LogLevel == "debug" {
|
||||
sctx.registerTrace()
|
||||
}
|
||||
sctxs[addr] = sctx
|
||||
}
|
||||
return sctxs, nil
|
||||
}
|
||||
|
||||
func resolveUrl(u url.URL) (addr string, secure bool, network string) {
|
||||
addr = u.Host
|
||||
network = "tcp"
|
||||
if u.Scheme == "unix" || u.Scheme == "unixs" {
|
||||
addr = u.Host + u.Path
|
||||
network = "unix"
|
||||
}
|
||||
secure = u.Scheme == "https" || u.Scheme == "unixs"
|
||||
return addr, secure, network
|
||||
}
|
||||
|
||||
func (e *Etcd) serveClients() (err error) {
|
||||
if !e.cfg.ClientTLSInfo.Empty() {
|
||||
e.cfg.logger.Info(
|
||||
@ -725,15 +766,69 @@ func (e *Etcd) serveClients() (err error) {
|
||||
}))
|
||||
}
|
||||
|
||||
splitHttp := false
|
||||
for _, sctx := range e.sctxs {
|
||||
if sctx.httpOnly {
|
||||
splitHttp = true
|
||||
}
|
||||
}
|
||||
|
||||
// start client servers in each goroutine
|
||||
for _, sctx := range e.sctxs {
|
||||
go func(s *serveCtx) {
|
||||
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, gopts...))
|
||||
e.errHandler(s.serve(e.Server, &e.cfg.ClientTLSInfo, h, e.errHandler, e.grpcGatewayDial(splitHttp), splitHttp, gopts...))
|
||||
}(sctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Etcd) grpcGatewayDial(splitHttp bool) (grpcDial func(ctx context.Context) (*grpc.ClientConn, error)) {
|
||||
if !e.cfg.EnableGRPCGateway {
|
||||
return nil
|
||||
}
|
||||
sctx := e.pickGrpcGatewayServeContext(splitHttp)
|
||||
addr := sctx.addr
|
||||
if network := sctx.network; network == "unix" {
|
||||
// explicitly define unix network for gRPC socket support
|
||||
addr = fmt.Sprintf("%s://%s", network, addr)
|
||||
}
|
||||
|
||||
opts := []grpc.DialOption{grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32))}
|
||||
if sctx.secure {
|
||||
tlscfg, tlsErr := e.cfg.ClientTLSInfo.ServerConfig()
|
||||
if tlsErr != nil {
|
||||
return func(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
return nil, tlsErr
|
||||
}
|
||||
}
|
||||
dtls := tlscfg.Clone()
|
||||
// trust local server
|
||||
dtls.InsecureSkipVerify = true
|
||||
bundle := credentials.NewBundle(credentials.Config{TLSConfig: dtls})
|
||||
opts = append(opts, grpc.WithTransportCredentials(bundle.TransportCredentials()))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
}
|
||||
|
||||
return func(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
conn, err := grpc.DialContext(ctx, addr, opts...)
|
||||
if err != nil {
|
||||
sctx.lg.Error("grpc gateway failed to dial", zap.String("addr", addr), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Etcd) pickGrpcGatewayServeContext(splitHttp bool) *serveCtx {
|
||||
for _, sctx := range e.sctxs {
|
||||
if !splitHttp || !sctx.httpOnly {
|
||||
return sctx
|
||||
}
|
||||
}
|
||||
panic("Expect at least one context able to serve grpc")
|
||||
}
|
||||
|
||||
func (e *Etcd) serveMetrics() (err error) {
|
||||
if e.cfg.Metrics == "extensive" {
|
||||
grpc_prometheus.EnableHandlingTimeHistogram()
|
||||
|
@ -19,14 +19,12 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
defaultLog "log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
etcdservergw "go.etcd.io/etcd/api/v3/etcdserverpb/gw"
|
||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||
"go.etcd.io/etcd/client/v3/credentials"
|
||||
"go.etcd.io/etcd/pkg/v3/debugutil"
|
||||
"go.etcd.io/etcd/pkg/v3/httputil"
|
||||
"go.etcd.io/etcd/server/v3/config"
|
||||
@ -50,12 +48,15 @@ import (
|
||||
)
|
||||
|
||||
type serveCtx struct {
|
||||
lg *zap.Logger
|
||||
l net.Listener
|
||||
lg *zap.Logger
|
||||
l net.Listener
|
||||
|
||||
scheme string
|
||||
addr string
|
||||
network string
|
||||
secure bool
|
||||
insecure bool
|
||||
httpOnly bool
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
@ -93,6 +94,8 @@ func (sctx *serveCtx) serve(
|
||||
tlsinfo *transport.TLSInfo,
|
||||
handler http.Handler,
|
||||
errHandler func(error),
|
||||
grpcDialForRestGatewayBackends func(ctx context.Context) (*grpc.ClientConn, error),
|
||||
splitHttp bool,
|
||||
gopts ...grpc.ServerOption) (err error) {
|
||||
logger := defaultLog.New(ioutil.Discard, "etcdhttp", 0)
|
||||
<-s.ReadyNotify()
|
||||
@ -100,115 +103,166 @@ func (sctx *serveCtx) serve(
|
||||
sctx.lg.Info("ready to serve client requests")
|
||||
|
||||
m := cmux.New(sctx.l)
|
||||
var server func() error
|
||||
onlyGRPC := splitHttp && !sctx.httpOnly
|
||||
onlyHttp := splitHttp && sctx.httpOnly
|
||||
grpcEnabled := !onlyHttp
|
||||
httpEnabled := !onlyGRPC
|
||||
|
||||
v3c := v3client.New(s)
|
||||
servElection := v3election.NewElectionServer(v3c)
|
||||
servLock := v3lock.NewLockServer(v3c)
|
||||
|
||||
var gs *grpc.Server
|
||||
defer func() {
|
||||
if err != nil && gs != nil {
|
||||
gs.Stop()
|
||||
// Make sure serversC is closed even if we prematurely exit the function.
|
||||
defer close(sctx.serversC)
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
// GRPC gateway connects to grpc server via connection provided by grpc dial.
|
||||
gwmux, err = sctx.registerGateway(grpcDialForRestGatewayBackends)
|
||||
if err != nil {
|
||||
sctx.lg.Error("registerGateway failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}()
|
||||
}
|
||||
var traffic string
|
||||
switch {
|
||||
case onlyGRPC:
|
||||
traffic = "grpc"
|
||||
case onlyHttp:
|
||||
traffic = "http"
|
||||
default:
|
||||
traffic = "grpc+http"
|
||||
}
|
||||
|
||||
if sctx.insecure {
|
||||
gs = v3rpc.Server(s, nil, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
grpcl := m.Match(cmux.HTTP2())
|
||||
go func() { errHandler(gs.Serve(grpcl)) }()
|
||||
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
gwmux, err = sctx.registerGateway([]grpc.DialOption{grpc.WithInsecure()})
|
||||
if err != nil {
|
||||
var gs *grpc.Server
|
||||
var srv *http.Server
|
||||
if httpEnabled {
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
srv = &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
ErrorLog: logger, // do not log user error
|
||||
}
|
||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure http server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
|
||||
srvhttp := &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
ErrorLog: logger, // do not log user error
|
||||
if grpcEnabled {
|
||||
gs = v3rpc.Server(s, nil, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
defer func(gs *grpc.Server) {
|
||||
if err != nil {
|
||||
sctx.lg.Warn("stopping insecure grpc server due to error", zap.Error(err))
|
||||
gs.Stop()
|
||||
sctx.lg.Warn("stopped insecure grpc server due to error", zap.Error(err))
|
||||
}
|
||||
}(gs)
|
||||
}
|
||||
if err := configureHttpServer(srvhttp, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure http server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
httpl := m.Match(cmux.HTTP1())
|
||||
go func() { errHandler(srvhttp.Serve(httpl)) }()
|
||||
if onlyGRPC {
|
||||
server = func() error {
|
||||
return gs.Serve(sctx.l)
|
||||
}
|
||||
} else {
|
||||
server = m.Serve
|
||||
|
||||
sctx.serversC <- &servers{grpc: gs, http: srvhttp}
|
||||
httpl := m.Match(cmux.HTTP1())
|
||||
go func(srvhttp *http.Server, tlsLis net.Listener) {
|
||||
errHandler(srvhttp.Serve(tlsLis))
|
||||
}(srv, httpl)
|
||||
|
||||
if grpcEnabled {
|
||||
grpcl := m.Match(cmux.HTTP2())
|
||||
go func(gs *grpc.Server, l net.Listener) {
|
||||
errHandler(gs.Serve(l))
|
||||
}(gs, grpcl)
|
||||
}
|
||||
}
|
||||
|
||||
sctx.serversC <- &servers{grpc: gs, http: srv}
|
||||
sctx.lg.Info(
|
||||
"serving client traffic insecurely; this is strongly discouraged!",
|
||||
zap.String("traffic", traffic),
|
||||
zap.String("address", sctx.l.Addr().String()),
|
||||
)
|
||||
}
|
||||
|
||||
if sctx.secure {
|
||||
var gs *grpc.Server
|
||||
var srv *http.Server
|
||||
|
||||
tlscfg, tlsErr := tlsinfo.ServerConfig()
|
||||
if tlsErr != nil {
|
||||
return tlsErr
|
||||
}
|
||||
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
handler = grpcHandlerFunc(gs, handler)
|
||||
|
||||
var gwmux *gw.ServeMux
|
||||
if s.Cfg.EnableGRPCGateway {
|
||||
dtls := tlscfg.Clone()
|
||||
// trust local server
|
||||
dtls.InsecureSkipVerify = true
|
||||
bundle := credentials.NewBundle(credentials.Config{TLSConfig: dtls})
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(bundle.TransportCredentials())}
|
||||
gwmux, err = sctx.registerGateway(opts)
|
||||
if err != nil {
|
||||
if grpcEnabled {
|
||||
gs = v3rpc.Server(s, tlscfg, nil, gopts...)
|
||||
v3electionpb.RegisterElectionServer(gs, servElection)
|
||||
v3lockpb.RegisterLockServer(gs, servLock)
|
||||
if sctx.serviceRegister != nil {
|
||||
sctx.serviceRegister(gs)
|
||||
}
|
||||
defer func(gs *grpc.Server) {
|
||||
if err != nil {
|
||||
sctx.lg.Warn("stopping secure grpc server due to error", zap.Error(err))
|
||||
gs.Stop()
|
||||
sctx.lg.Warn("stopped secure grpc server due to error", zap.Error(err))
|
||||
}
|
||||
}(gs)
|
||||
}
|
||||
if httpEnabled {
|
||||
if grpcEnabled {
|
||||
handler = grpcHandlerFunc(gs, handler)
|
||||
}
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
|
||||
srv = &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
TLSConfig: tlscfg,
|
||||
ErrorLog: logger, // do not log user error
|
||||
}
|
||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure https server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var tlsl net.Listener
|
||||
tlsl, err = transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: add debug flag; enable logging when debug flag is set
|
||||
httpmux := sctx.createMux(gwmux, handler)
|
||||
if onlyGRPC {
|
||||
server = func() error { return gs.Serve(sctx.l) }
|
||||
} else {
|
||||
server = m.Serve
|
||||
|
||||
srv := &http.Server{
|
||||
Handler: createAccessController(sctx.lg, s, httpmux),
|
||||
TLSConfig: tlscfg,
|
||||
ErrorLog: logger, // do not log user error
|
||||
tlsl, err := transport.NewTLSListener(m.Match(cmux.Any()), tlsinfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(srvhttp *http.Server, tlsl net.Listener) {
|
||||
errHandler(srvhttp.Serve(tlsl))
|
||||
}(srv, tlsl)
|
||||
}
|
||||
if err := configureHttpServer(srv, s.Cfg); err != nil {
|
||||
sctx.lg.Error("Configure https server failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
go func() { errHandler(srv.Serve(tlsl)) }()
|
||||
|
||||
sctx.serversC <- &servers{secure: true, grpc: gs, http: srv}
|
||||
sctx.lg.Info(
|
||||
"serving client traffic securely",
|
||||
zap.String("traffic", traffic),
|
||||
zap.String("address", sctx.l.Addr().String()),
|
||||
)
|
||||
}
|
||||
|
||||
close(sctx.serversC)
|
||||
return m.Serve()
|
||||
return server()
|
||||
}
|
||||
|
||||
func configureHttpServer(srv *http.Server, cfg config.ServerConfig) error {
|
||||
// todo (ahrtr): should we support configuring other parameters in the future as well?
|
||||
return http2.ConfigureServer(srv, &http2.Server{
|
||||
MaxConcurrentStreams: cfg.MaxConcurrentStreams,
|
||||
// Override to avoid using priority scheduler which is affected by https://github.com/golang/go/issues/58804.
|
||||
NewWriteScheduler: http2.NewRandomWriteScheduler,
|
||||
})
|
||||
}
|
||||
|
||||
@ -231,20 +285,10 @@ func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Ha
|
||||
|
||||
type registerHandlerFunc func(context.Context, *gw.ServeMux, *grpc.ClientConn) error
|
||||
|
||||
func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, error) {
|
||||
func (sctx *serveCtx) registerGateway(dial func(ctx context.Context) (*grpc.ClientConn, error)) (*gw.ServeMux, error) {
|
||||
ctx := sctx.ctx
|
||||
|
||||
addr := sctx.addr
|
||||
if network := sctx.network; network == "unix" {
|
||||
// explicitly define unix network for gRPC socket support
|
||||
addr = fmt.Sprintf("%s://%s", network, addr)
|
||||
}
|
||||
|
||||
opts = append(opts, grpc.WithDefaultCallOptions([]grpc.CallOption{
|
||||
grpc.MaxCallRecvMsgSize(math.MaxInt32),
|
||||
}...))
|
||||
|
||||
conn, err := grpc.DialContext(ctx, addr, opts...)
|
||||
conn, err := dial(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -279,6 +323,18 @@ func (sctx *serveCtx) registerGateway(opts []grpc.DialOption) (*gw.ServeMux, err
|
||||
return gwmux, nil
|
||||
}
|
||||
|
||||
type wsProxyZapLogger struct {
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
func (w wsProxyZapLogger) Warnln(i ...interface{}) {
|
||||
w.Warn(fmt.Sprint(i...))
|
||||
}
|
||||
|
||||
func (w wsProxyZapLogger) Debugln(i ...interface{}) {
|
||||
w.Debug(fmt.Sprint(i...))
|
||||
}
|
||||
|
||||
func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.ServeMux {
|
||||
httpmux := http.NewServeMux()
|
||||
for path, h := range sctx.userHandlers {
|
||||
@ -298,6 +354,7 @@ func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http.
|
||||
},
|
||||
),
|
||||
wsproxy.WithMaxRespBodyBufferSize(0x7fffffff),
|
||||
wsproxy.WithLogger(wsProxyZapLogger{sctx.lg}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ func TestStartEtcdWrongToken(t *testing.T) {
|
||||
urls := newEmbedURLs(2)
|
||||
curls := []url.URL{urls[0]}
|
||||
purls := []url.URL{urls[1]}
|
||||
cfg.LCUrls, cfg.ACUrls = curls, curls
|
||||
cfg.LPUrls, cfg.APUrls = purls, purls
|
||||
cfg.ListenClientUrls, cfg.AdvertiseClientUrls = curls, curls
|
||||
cfg.ListenPeerUrls, cfg.AdvertisePeerUrls = purls, purls
|
||||
cfg.InitialCluster = ""
|
||||
for i := range purls {
|
||||
cfg.InitialCluster += ",default=" + purls[i].String()
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
|
||||
"go.etcd.io/etcd/api/v3/version"
|
||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
|
||||
"go.etcd.io/etcd/pkg/v3/flags"
|
||||
cconfig "go.etcd.io/etcd/server/v3/config"
|
||||
"go.etcd.io/etcd/server/v3/embed"
|
||||
@ -145,7 +146,11 @@ func newConfig() *config {
|
||||
)
|
||||
fs.Var(
|
||||
flags.NewUniqueURLsWithExceptions(embed.DefaultListenClientURLs, ""), "listen-client-urls",
|
||||
"List of URLs to listen on for client traffic.",
|
||||
"List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.",
|
||||
)
|
||||
fs.Var(
|
||||
flags.NewUniqueURLsWithExceptions("", ""), "listen-client-http-urls",
|
||||
"List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.",
|
||||
)
|
||||
fs.Var(
|
||||
flags.NewUniqueURLsWithExceptions("", ""),
|
||||
@ -237,6 +242,8 @@ func newConfig() *config {
|
||||
fs.StringVar(&cfg.ec.PeerTLSInfo.AllowedHostname, "peer-cert-allowed-hostname", "", "Allowed TLS hostname for inter peer 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.ec.PeerTLSInfo.SkipClientSANVerify, "experimental-peer-skip-client-san-verification", false, "Skip verification of SAN field in client certificate for peer connections.")
|
||||
fs.StringVar(&cfg.ec.TlsMinVersion, "tls-min-version", string(tlsutil.TLSVersion12), "Minimum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3.")
|
||||
fs.StringVar(&cfg.ec.TlsMaxVersion, "tls-max-version", string(tlsutil.TLSVersionDefault), "Maximum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3 (empty defers to Go).")
|
||||
|
||||
fs.Var(
|
||||
flags.NewUniqueURLsWithExceptions("*", "*"),
|
||||
@ -389,10 +396,11 @@ func (cfg *config) configFromCmdLine() error {
|
||||
lg.Info(fmt.Sprintf("raft-write-timeout increased to minimum value: %v", rafthttp.DefaultConnWriteTimeout))
|
||||
}
|
||||
|
||||
cfg.ec.LPUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
|
||||
cfg.ec.APUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
|
||||
cfg.ec.LCUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
|
||||
cfg.ec.ACUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
|
||||
cfg.ec.ListenPeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-peer-urls")
|
||||
cfg.ec.AdvertisePeerUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "initial-advertise-peer-urls")
|
||||
cfg.ec.ListenClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-urls")
|
||||
cfg.ec.ListenClientHttpUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-client-http-urls")
|
||||
cfg.ec.AdvertiseClientUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "advertise-client-urls")
|
||||
cfg.ec.ListenMetricsUrls = flags.UniqueURLsFromFlag(cfg.cf.flagSet, "listen-metrics-urls")
|
||||
|
||||
cfg.ec.CORS = flags.UniqueURLsMapFromFlag(cfg.cf.flagSet, "cors")
|
||||
@ -413,7 +421,7 @@ func (cfg *config) configFromCmdLine() error {
|
||||
// disable default advertise-client-urls if lcurls is set
|
||||
missingAC := flags.IsSet(cfg.cf.flagSet, "listen-client-urls") && !flags.IsSet(cfg.cf.flagSet, "advertise-client-urls")
|
||||
if !cfg.mayBeProxy() && missingAC {
|
||||
cfg.ec.ACUrls = nil
|
||||
cfg.ec.AdvertiseClientUrls = nil
|
||||
}
|
||||
|
||||
// disable default initial-cluster if discovery is set
|
||||
|
@ -36,6 +36,7 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
||||
"-snapshot-count=10",
|
||||
"-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",
|
||||
// it should be set if -listen-client-urls is set
|
||||
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
|
||||
}
|
||||
@ -51,14 +52,15 @@ func TestConfigParsingMemberFlags(t *testing.T) {
|
||||
|
||||
func TestConfigFileMemberFields(t *testing.T) {
|
||||
yc := struct {
|
||||
Dir string `json:"data-dir"`
|
||||
MaxSnapFiles uint `json:"max-snapshots"`
|
||||
MaxWalFiles uint `json:"max-wals"`
|
||||
Name string `json:"name"`
|
||||
SnapshotCount uint64 `json:"snapshot-count"`
|
||||
LPUrls string `json:"listen-peer-urls"`
|
||||
LCUrls string `json:"listen-client-urls"`
|
||||
AcurlsCfgFile string `json:"advertise-client-urls"`
|
||||
Dir string `json:"data-dir"`
|
||||
MaxSnapFiles uint `json:"max-snapshots"`
|
||||
MaxWalFiles uint `json:"max-wals"`
|
||||
Name string `json:"name"`
|
||||
SnapshotCount uint64 `json:"snapshot-count"`
|
||||
ListenPeerUrls string `json:"listen-peer-urls"`
|
||||
ListenClientUrls string `json:"listen-client-urls"`
|
||||
ListenClientHttpUrls string `json:"listen-client-http-urls"`
|
||||
AdvertiseClientUrls string `json:"advertise-client-urls"`
|
||||
}{
|
||||
"testdir",
|
||||
10,
|
||||
@ -67,6 +69,7 @@ func TestConfigFileMemberFields(t *testing.T) {
|
||||
10,
|
||||
"http://localhost:8000,https://localhost:8001",
|
||||
"http://localhost:7000,https://localhost:7001",
|
||||
"http://localhost:7002,https://localhost:7003",
|
||||
"http://localhost:7000,https://localhost:7001",
|
||||
}
|
||||
|
||||
@ -513,13 +516,14 @@ func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
|
||||
|
||||
func validateMemberFlags(t *testing.T, cfg *config) {
|
||||
wcfg := &embed.Config{
|
||||
Dir: "testdir",
|
||||
LPUrls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
||||
LCUrls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
||||
MaxSnapFiles: 10,
|
||||
MaxWalFiles: 10,
|
||||
Name: "testname",
|
||||
SnapshotCount: 10,
|
||||
Dir: "testdir",
|
||||
ListenPeerUrls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
|
||||
ListenClientUrls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
|
||||
ListenClientHttpUrls: []url.URL{{Scheme: "http", Host: "localhost:7002"}, {Scheme: "https", Host: "localhost:7003"}},
|
||||
MaxSnapFiles: 10,
|
||||
MaxWalFiles: 10,
|
||||
Name: "testname",
|
||||
SnapshotCount: 10,
|
||||
}
|
||||
|
||||
if cfg.ec.Dir != wcfg.Dir {
|
||||
@ -537,18 +541,21 @@ func validateMemberFlags(t *testing.T, cfg *config) {
|
||||
if cfg.ec.SnapshotCount != wcfg.SnapshotCount {
|
||||
t.Errorf("snapcount = %v, want %v", cfg.ec.SnapshotCount, wcfg.SnapshotCount)
|
||||
}
|
||||
if !reflect.DeepEqual(cfg.ec.LPUrls, wcfg.LPUrls) {
|
||||
t.Errorf("listen-peer-urls = %v, want %v", cfg.ec.LPUrls, wcfg.LPUrls)
|
||||
if !reflect.DeepEqual(cfg.ec.ListenPeerUrls, wcfg.ListenPeerUrls) {
|
||||
t.Errorf("listen-peer-urls = %v, want %v", cfg.ec.ListenPeerUrls, wcfg.ListenPeerUrls)
|
||||
}
|
||||
if !reflect.DeepEqual(cfg.ec.LCUrls, wcfg.LCUrls) {
|
||||
t.Errorf("listen-client-urls = %v, want %v", cfg.ec.LCUrls, wcfg.LCUrls)
|
||||
if !reflect.DeepEqual(cfg.ec.ListenClientUrls, wcfg.ListenClientUrls) {
|
||||
t.Errorf("listen-client-urls = %v, want %v", cfg.ec.ListenClientUrls, wcfg.ListenClientUrls)
|
||||
}
|
||||
if !reflect.DeepEqual(cfg.ec.ListenClientHttpUrls, wcfg.ListenClientHttpUrls) {
|
||||
t.Errorf("listen-client-http-urls = %v, want %v", cfg.ec.ListenClientHttpUrls, wcfg.ListenClientHttpUrls)
|
||||
}
|
||||
}
|
||||
|
||||
func validateClusteringFlags(t *testing.T, cfg *config) {
|
||||
wcfg := newConfig()
|
||||
wcfg.ec.APUrls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
|
||||
wcfg.ec.ACUrls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
|
||||
wcfg.ec.AdvertisePeerUrls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
|
||||
wcfg.ec.AdvertiseClientUrls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
|
||||
wcfg.ec.ClusterState = embed.ClusterStateFlagExisting
|
||||
wcfg.cf.fallback.Set(fallbackFlagExit)
|
||||
wcfg.ec.InitialCluster = "0=http://localhost:8000"
|
||||
@ -566,11 +573,11 @@ func validateClusteringFlags(t *testing.T, cfg *config) {
|
||||
if cfg.ec.InitialClusterToken != wcfg.ec.InitialClusterToken {
|
||||
t.Errorf("initialClusterToken = %v, want %v", cfg.ec.InitialClusterToken, wcfg.ec.InitialClusterToken)
|
||||
}
|
||||
if !reflect.DeepEqual(cfg.ec.APUrls, wcfg.ec.APUrls) {
|
||||
t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.ec.APUrls, wcfg.ec.APUrls)
|
||||
if !reflect.DeepEqual(cfg.ec.AdvertisePeerUrls, wcfg.ec.AdvertisePeerUrls) {
|
||||
t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.ec.AdvertisePeerUrls, wcfg.ec.AdvertisePeerUrls)
|
||||
}
|
||||
if !reflect.DeepEqual(cfg.ec.ACUrls, wcfg.ec.ACUrls) {
|
||||
t.Errorf("advertise-client-urls = %v, want %v", cfg.ec.ACUrls, wcfg.ec.ACUrls)
|
||||
if !reflect.DeepEqual(cfg.ec.AdvertiseClientUrls, wcfg.ec.AdvertiseClientUrls) {
|
||||
t.Errorf("advertise-client-urls = %v, want %v", cfg.ec.AdvertiseClientUrls, wcfg.ec.AdvertiseClientUrls)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,7 +193,7 @@ func startEtcdOrProxyV2(args []string) {
|
||||
if cfg.ec.InitialCluster == cfg.ec.InitialClusterFromName(cfg.ec.Name) {
|
||||
lg.Warn("forgot to set --initial-cluster?")
|
||||
}
|
||||
if types.URLs(cfg.ec.APUrls).String() == embed.DefaultInitialAdvertisePeerURLs {
|
||||
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 {
|
||||
@ -276,7 +276,7 @@ func startProxy(cfg *config) error {
|
||||
}
|
||||
|
||||
cfg.ec.Dir = filepath.Join(cfg.ec.Dir, "proxy")
|
||||
err = fileutil.TouchDirAll(cfg.ec.Dir)
|
||||
err = fileutil.TouchDirAll(lg, cfg.ec.Dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -389,11 +389,11 @@ func startProxy(cfg *config) error {
|
||||
|
||||
// setup self signed certs when serving https
|
||||
cHosts, cTLS := []string{}, false
|
||||
for _, u := range cfg.ec.LCUrls {
|
||||
for _, u := range cfg.ec.ListenClientUrls {
|
||||
cHosts = append(cHosts, u.Host)
|
||||
cTLS = cTLS || u.Scheme == "https"
|
||||
}
|
||||
for _, u := range cfg.ec.ACUrls {
|
||||
for _, u := range cfg.ec.AdvertiseClientUrls {
|
||||
cHosts = append(cHosts, u.Host)
|
||||
cTLS = cTLS || u.Scheme == "https"
|
||||
}
|
||||
@ -406,7 +406,7 @@ func startProxy(cfg *config) error {
|
||||
}
|
||||
|
||||
// Start a proxy server goroutine for each listen address
|
||||
for _, u := range cfg.ec.LCUrls {
|
||||
for _, u := range cfg.ec.ListenClientUrls {
|
||||
l, err := transport.NewListener(u.Host, u.Scheme, &listenerTLS)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
|
||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/client/v3/leasing"
|
||||
@ -41,12 +42,12 @@ import (
|
||||
"go.etcd.io/etcd/server/v3/etcdserver/api/v3election/v3electionpb"
|
||||
"go.etcd.io/etcd/server/v3/etcdserver/api/v3lock/v3lockpb"
|
||||
"go.etcd.io/etcd/server/v3/proxy/grpcproxy"
|
||||
"go.uber.org/zap/zapgrpc"
|
||||
|
||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||
"github.com/soheilhy/cmux"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapgrpc"
|
||||
"golang.org/x/net/http2"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
@ -74,12 +75,13 @@ var (
|
||||
|
||||
// tls for clients connecting to proxy
|
||||
|
||||
grpcProxyListenCA string
|
||||
grpcProxyListenCert string
|
||||
grpcProxyListenKey string
|
||||
grpcProxyListenAutoTLS bool
|
||||
grpcProxyListenCRL string
|
||||
selfSignedCertValidity uint
|
||||
grpcProxyListenCA string
|
||||
grpcProxyListenCert string
|
||||
grpcProxyListenKey string
|
||||
grpcProxyListenCipherSuites []string
|
||||
grpcProxyListenAutoTLS bool
|
||||
grpcProxyListenCRL string
|
||||
selfSignedCertValidity uint
|
||||
|
||||
grpcProxyAdvertiseClientURL string
|
||||
grpcProxyResolverPrefix string
|
||||
@ -154,6 +156,7 @@ func newGRPCProxyStartCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(&grpcProxyListenCert, "cert-file", "", "identify secure connections to the proxy using this TLS certificate file")
|
||||
cmd.Flags().StringVar(&grpcProxyListenKey, "key-file", "", "identify secure connections to the proxy using this TLS key file")
|
||||
cmd.Flags().StringVar(&grpcProxyListenCA, "trusted-ca-file", "", "verify certificates of TLS-enabled secure proxy using this CA bundle")
|
||||
cmd.Flags().StringSliceVar(&grpcProxyListenCipherSuites, "listen-cipher-suites", grpcProxyListenCipherSuites, "Comma-separated list of supported TLS cipher suites between client/proxy (empty will be auto-populated by Go).")
|
||||
cmd.Flags().BoolVar(&grpcProxyListenAutoTLS, "auto-tls", false, "proxy TLS using generated certificates")
|
||||
cmd.Flags().StringVar(&grpcProxyListenCRL, "client-crl-file", "", "proxy client certificate revocation list file.")
|
||||
cmd.Flags().UintVar(&selfSignedCertValidity, "self-signed-cert-validity", 1, "The validity period of the proxy certificates, unit is year")
|
||||
@ -187,21 +190,28 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
|
||||
// The proxy itself (ListenCert) can have not-empty CN.
|
||||
// The empty CN is required for grpcProxyCert.
|
||||
// Please see https://github.com/etcd-io/etcd/issues/11970#issuecomment-687875315 for more context.
|
||||
tlsinfo := newTLS(grpcProxyListenCA, grpcProxyListenCert, grpcProxyListenKey, false)
|
||||
|
||||
if tlsinfo == nil && grpcProxyListenAutoTLS {
|
||||
tlsInfo := newTLS(grpcProxyListenCA, grpcProxyListenCert, grpcProxyListenKey, false)
|
||||
if len(grpcProxyListenCipherSuites) > 0 {
|
||||
cs, err := tlsutil.GetCipherSuites(grpcProxyListenCipherSuites)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tlsInfo.CipherSuites = cs
|
||||
}
|
||||
if tlsInfo == nil && grpcProxyListenAutoTLS {
|
||||
host := []string{"https://" + grpcProxyListenAddr}
|
||||
dir := filepath.Join(grpcProxyDataDir, "fixtures", "proxy")
|
||||
autoTLS, err := transport.SelfCert(lg, dir, host, selfSignedCertValidity)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tlsinfo = &autoTLS
|
||||
tlsInfo = &autoTLS
|
||||
}
|
||||
if tlsinfo != nil {
|
||||
lg.Info("gRPC proxy server TLS", zap.String("tls-info", fmt.Sprintf("%+v", tlsinfo)))
|
||||
|
||||
if tlsInfo != nil {
|
||||
lg.Info("gRPC proxy server TLS", zap.String("tls-info", fmt.Sprintf("%+v", tlsInfo)))
|
||||
}
|
||||
m := mustListenCMux(lg, tlsinfo)
|
||||
m := mustListenCMux(lg, tlsInfo)
|
||||
grpcl := m.Match(cmux.HTTP2())
|
||||
defer func() {
|
||||
grpcl.Close()
|
||||
@ -214,11 +224,11 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
|
||||
// TODO: The mechanism should be refactored to use internal connection.
|
||||
var proxyClient *clientv3.Client
|
||||
if grpcProxyAdvertiseClientURL != "" {
|
||||
proxyClient = mustNewProxyClient(lg, tlsinfo)
|
||||
proxyClient = mustNewProxyClient(lg, tlsInfo)
|
||||
}
|
||||
httpClient := mustNewHTTPClient(lg)
|
||||
|
||||
srvhttp, httpl := mustHTTPListener(lg, m, tlsinfo, client, proxyClient)
|
||||
srvhttp, httpl := mustHTTPListener(lg, m, tlsInfo, client, proxyClient)
|
||||
|
||||
if err := http2.ConfigureServer(srvhttp, &http2.Server{
|
||||
MaxConcurrentStreams: maxConcurrentStreams,
|
||||
@ -231,7 +241,7 @@ func startGRPCProxy(cmd *cobra.Command, args []string) {
|
||||
go func() { errc <- srvhttp.Serve(httpl) }()
|
||||
go func() { errc <- m.Serve() }()
|
||||
if len(grpcProxyMetricsListenAddr) > 0 {
|
||||
mhttpl := mustMetricsListener(lg, tlsinfo)
|
||||
mhttpl := mustMetricsListener(lg, tlsInfo)
|
||||
go func() {
|
||||
mux := http.NewServeMux()
|
||||
grpcproxy.HandleMetrics(mux, httpClient, client.Endpoints())
|
||||
|
@ -63,7 +63,9 @@ Member:
|
||||
--listen-peer-urls 'http://localhost:2380'
|
||||
List of URLs to listen on for peer traffic.
|
||||
--listen-client-urls 'http://localhost:2379'
|
||||
List of URLs to listen on for client traffic.
|
||||
List of URLs to listen on for client grpc traffic and http as long as --listen-client-http-urls is not specified.
|
||||
--listen-client-http-urls ''
|
||||
List of URLs to listen on for http only client traffic. Enabling this flag removes http services from --listen-client-urls.
|
||||
--max-snapshots '` + strconv.Itoa(embed.DefaultMaxSnapshots) + `'
|
||||
Maximum number of snapshot files to retain (0 is unlimited).
|
||||
--max-wals '` + strconv.Itoa(embed.DefaultMaxWALs) + `'
|
||||
@ -174,6 +176,10 @@ Security:
|
||||
Comma-separated whitelist of origins for CORS, or cross-origin resource sharing, (empty or * means allow all).
|
||||
--host-whitelist '*'
|
||||
Acceptable hostnames from HTTP client requests, if server is not secure (empty or * means allow all).
|
||||
--tls-min-version 'TLS1.2'
|
||||
Minimum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3.
|
||||
--tls-max-version ''
|
||||
Maximum TLS version supported by etcd. Possible values: TLS1.2, TLS1.3 (empty will be auto-populated by Go).
|
||||
|
||||
Auth:
|
||||
--auth-token 'simple'
|
||||
|
@ -712,7 +712,7 @@ func TestServeMembers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consolidate **ALL** fake server implementations and add no leader test case.
|
||||
// TestServeLeader TODO: consolidate **ALL** fake server implementations and add no leader test case.
|
||||
func TestServeLeader(t *testing.T) {
|
||||
memb1 := membership.Member{ID: 1, Attributes: membership.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
|
||||
memb2 := membership.Member{ID: 2, Attributes: membership.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"go.etcd.io/etcd/client/pkg/v3/testutil"
|
||||
)
|
||||
|
||||
// Ensure that a successful Get is recorded in the stats.
|
||||
// TestStoreStatsGetSuccess to ensure that a successful Get is recorded in the stats.
|
||||
func TestStoreStatsGetSuccess(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -29,7 +29,7 @@ func TestStoreStatsGetSuccess(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.GetSuccess, "")
|
||||
}
|
||||
|
||||
// Ensure that a failed Get is recorded in the stats.
|
||||
// TestStoreStatsGetFail to ensure that a failed Get is recorded in the stats.
|
||||
func TestStoreStatsGetFail(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -37,14 +37,14 @@ func TestStoreStatsGetFail(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.GetFail, "")
|
||||
}
|
||||
|
||||
// Ensure that a successful Create is recorded in the stats.
|
||||
// TestStoreStatsCreateSuccess to ensure that a successful Create is recorded in the stats.
|
||||
func TestStoreStatsCreateSuccess(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.CreateSuccess, "")
|
||||
}
|
||||
|
||||
// Ensure that a failed Create is recorded in the stats.
|
||||
// TestStoreStatsCreateFail to ensure that a failed Create is recorded in the stats.
|
||||
func TestStoreStatsCreateFail(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -52,7 +52,7 @@ func TestStoreStatsCreateFail(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.CreateFail, "")
|
||||
}
|
||||
|
||||
// Ensure that a successful Update is recorded in the stats.
|
||||
// TestStoreStatsUpdateSuccess to ensure that a successful Update is recorded in the stats.
|
||||
func TestStoreStatsUpdateSuccess(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -60,14 +60,14 @@ func TestStoreStatsUpdateSuccess(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.UpdateSuccess, "")
|
||||
}
|
||||
|
||||
// Ensure that a failed Update is recorded in the stats.
|
||||
// TestStoreStatsUpdateFail to ensure that a failed Update is recorded in the stats.
|
||||
func TestStoreStatsUpdateFail(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Update("/foo", "bar", TTLOptionSet{ExpireTime: Permanent})
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.UpdateFail, "")
|
||||
}
|
||||
|
||||
// Ensure that a successful CAS is recorded in the stats.
|
||||
// TestStoreStatsCompareAndSwapSuccess to ensure that a successful CAS is recorded in the stats.
|
||||
func TestStoreStatsCompareAndSwapSuccess(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -75,7 +75,7 @@ func TestStoreStatsCompareAndSwapSuccess(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.CompareAndSwapSuccess, "")
|
||||
}
|
||||
|
||||
// Ensure that a failed CAS is recorded in the stats.
|
||||
// TestStoreStatsCompareAndSwapFail to ensure that a failed CAS is recorded in the stats.
|
||||
func TestStoreStatsCompareAndSwapFail(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -83,7 +83,7 @@ func TestStoreStatsCompareAndSwapFail(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.CompareAndSwapFail, "")
|
||||
}
|
||||
|
||||
// Ensure that a successful Delete is recorded in the stats.
|
||||
// TestStoreStatsDeleteSuccess to ensure that a successful Delete is recorded in the stats.
|
||||
func TestStoreStatsDeleteSuccess(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
|
||||
@ -91,14 +91,14 @@ func TestStoreStatsDeleteSuccess(t *testing.T) {
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.DeleteSuccess, "")
|
||||
}
|
||||
|
||||
// Ensure that a failed Delete is recorded in the stats.
|
||||
// TestStoreStatsDeleteFail to ensure that a failed Delete is recorded in the stats.
|
||||
func TestStoreStatsDeleteFail(t *testing.T) {
|
||||
s := newStore()
|
||||
s.Delete("/foo", false, false)
|
||||
testutil.AssertEqual(t, uint64(1), s.Stats.DeleteFail, "")
|
||||
}
|
||||
|
||||
//Ensure that the number of expirations is recorded in the stats.
|
||||
// TestStoreStatsExpireCount to ensure that the number of expirations is recorded in the stats.
|
||||
func TestStoreStatsExpireCount(t *testing.T) {
|
||||
s := newStore()
|
||||
fc := newFakeClock()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user