Compare commits
29 Commits
client/pkg
...
v3.0.2
Author | SHA1 | Date | |
---|---|---|---|
faeeb2fc75 | |||
d50c487132 | |||
b837feffe4 | |||
4d89640195 | |||
1292d453c3 | |||
ec20b381ed | |||
37cc3f5262 | |||
7f1940e5ed | |||
caccf8e5e6 | |||
ef65dfe2eb | |||
ff6c6916f2 | |||
3dfe8765d3 | |||
a4a52cb15d | |||
014970930a | |||
4628be982c | |||
ff55e5a188 | |||
bf0898266c | |||
b9d69f7698 | |||
6f48bda7ac | |||
316534e09e | |||
3cecbdb464 | |||
62f11e43ee | |||
064c1585ee | |||
15300a1eb8 | |||
58dd047ee4 | |||
4b42ea6cd7 | |||
53c27ae621 | |||
269de67bde | |||
8bbccf1047 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@
|
||||
/hack/insta-discovery/.env
|
||||
*.test
|
||||
tools/functional-tester/docker/bin
|
||||
hack/tls-setup/certs
|
||||
|
@ -4,7 +4,6 @@ go_import_path: github.com/coreos/etcd
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
@ -22,10 +21,6 @@ matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
exclude:
|
||||
- go: 1.5
|
||||
env: TARGET=arm
|
||||
- go: 1.5
|
||||
env: TARGET=ppc64le
|
||||
- go: 1.6
|
||||
env: TARGET=arm64
|
||||
- go: tip
|
||||
|
@ -1,10 +1,10 @@
|
||||
FROM alpine:latest
|
||||
|
||||
ADD bin/etcd /usr/local/bin/
|
||||
ADD bin/etcdctl /usr/local/bin/
|
||||
ADD etcd /usr/local/bin/
|
||||
ADD etcdctl /usr/local/bin/
|
||||
RUN mkdir -p /var/etcd/
|
||||
|
||||
EXPOSE 2379 2380
|
||||
|
||||
# Define default command.
|
||||
CMD ["/usr/local/bin/etcd"]
|
||||
# Define default entrypoint.
|
||||
ENTRYPOINT ["/usr/local/bin/etcd"]
|
||||
|
@ -25,7 +25,7 @@ curl -L http://localhost:2379/v3alpha/kv/range \
|
||||
|
||||
## Swagger
|
||||
|
||||
Generated [Swapper][swagger] API definitions can be found at [rpc.swagger.json][swagger-doc].
|
||||
Generated [Swagger][swagger] API definitions can be found at [rpc.swagger.json][swagger-doc].
|
||||
|
||||
[api-ref]: ./api_reference_v3.md
|
||||
[go-client]: https://github.com/coreos/etcd/tree/master/clientv3
|
||||
|
@ -4,5 +4,5 @@ For the most part, the etcd project is stable, but we are still moving fast! We
|
||||
|
||||
## The current experimental API/features are:
|
||||
|
||||
- v3 auth API: expect to be stale in 3.1 release
|
||||
- etcd gateway: expect to be stable in 3.1 release
|
||||
- v3 auth API: expect to be stable in 3.1 release
|
||||
- etcd gateway: expect to be stable in 3.1 release
|
||||
|
@ -11,7 +11,7 @@ The easiest way to get etcd is to use one of the pre-built release binaries whic
|
||||
## Build the latest version
|
||||
|
||||
For those wanting to try the very latest version, build etcd from the `master` branch.
|
||||
[Go](https://golang.org/) version 1.5+ is required to build the latest version of etcd.
|
||||
[Go](https://golang.org/) version 1.6+ (with HTTP2 support) is required to build the latest version of etcd.
|
||||
|
||||
Here are the commands to build an etcd binary from the `master` branch:
|
||||
|
||||
|
@ -8,7 +8,7 @@ In order to expose the etcd API to clients outside of Docker host, use the host
|
||||
|
||||
```
|
||||
# For each machine
|
||||
ETCD_VERSION=v3.0.0-beta.0
|
||||
ETCD_VERSION=v3.0.0
|
||||
TOKEN=my-etcd-token
|
||||
CLUSTER_STATE=new
|
||||
NAME_1=etcd-node-0
|
||||
|
@ -40,7 +40,7 @@ See [etcdctl][etcdctl] for a simple command line client.
|
||||
The easiest way to get etcd is to use one of the pre-built release binaries which are available for OSX, Linux, Windows, AppC (ACI), and Docker. Instructions for using these binaries are on the [GitHub releases page][github-release].
|
||||
|
||||
For those wanting to try the very latest version, you can build the latest version of etcd from the `master` branch.
|
||||
You will first need [*Go*](https://golang.org/) installed on your machine (version 1.5+ is required).
|
||||
You will first need [*Go*](https://golang.org/) installed on your machine (version 1.6+ is required).
|
||||
All development occurs on `master`, including new features and bug fixes.
|
||||
Bug fixes are first targeted at `master` and subsequently ported to release branches, as described in the [branch management][branch-management] guide.
|
||||
|
||||
|
@ -37,6 +37,10 @@ var (
|
||||
ErrClusterUnavailable = errors.New("client: etcd cluster is unavailable or misconfigured")
|
||||
ErrNoLeaderEndpoint = errors.New("client: no leader endpoint available")
|
||||
errTooManyRedirectChecks = errors.New("client: too many redirect checks")
|
||||
|
||||
// oneShotCtxValue is set on a context using WithValue(&oneShotValue) so
|
||||
// that Do() will not retry a request
|
||||
oneShotCtxValue interface{}
|
||||
)
|
||||
|
||||
var DefaultRequestTimeout = 5 * time.Second
|
||||
@ -335,6 +339,7 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
|
||||
var body []byte
|
||||
var err error
|
||||
cerr := &ClusterError{}
|
||||
isOneShot := ctx.Value(&oneShotCtxValue) != nil
|
||||
|
||||
for i := pinned; i < leps+pinned; i++ {
|
||||
k := i % leps
|
||||
@ -348,6 +353,9 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
|
||||
if err == context.Canceled || err == context.DeadlineExceeded {
|
||||
return nil, nil, err
|
||||
}
|
||||
if isOneShot {
|
||||
return nil, nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode/100 == 5 {
|
||||
@ -358,6 +366,9 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
|
||||
default:
|
||||
cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s returns server error [%s]", eps[k].String(), http.StatusText(resp.StatusCode)))
|
||||
}
|
||||
if isOneShot {
|
||||
return nil, nil, cerr.Errors[0]
|
||||
}
|
||||
continue
|
||||
}
|
||||
if k != pinned {
|
||||
|
134
client/integration/client_test.go
Normal file
134
client/integration/client_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/etcd/client"
|
||||
"github.com/coreos/etcd/integration"
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
)
|
||||
|
||||
// TestV2NoRetryEOF tests destructive api calls won't retry on a disconnection.
|
||||
func TestV2NoRetryEOF(t *testing.T) {
|
||||
defer testutil.AfterTest(t)
|
||||
// generate an EOF response; specify address so appears first in sorted ep list
|
||||
lEOF := integration.NewListenerWithAddr(t, fmt.Sprintf("eof:123.%d.sock", os.Getpid()))
|
||||
defer lEOF.Close()
|
||||
tries := uint32(0)
|
||||
go func() {
|
||||
for {
|
||||
conn, err := lEOF.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
atomic.AddUint32(&tries, 1)
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
eofURL := integration.UrlScheme + "://" + lEOF.Addr().String()
|
||||
cli := integration.MustNewHTTPClient(t, []string{eofURL, eofURL}, nil)
|
||||
kapi := client.NewKeysAPI(cli)
|
||||
for i, f := range noRetryList(kapi) {
|
||||
startTries := atomic.LoadUint32(&tries)
|
||||
if err := f(); err == nil {
|
||||
t.Errorf("#%d: expected EOF error, got nil", i)
|
||||
}
|
||||
endTries := atomic.LoadUint32(&tries)
|
||||
if startTries+1 != endTries {
|
||||
t.Errorf("#%d: expected 1 try, got %d", i, endTries-startTries)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2NoRetryNoLeader tests destructive api calls won't retry if given an error code.
|
||||
func TestV2NoRetryNoLeader(t *testing.T) {
|
||||
defer testutil.AfterTest(t)
|
||||
|
||||
lHttp := integration.NewListenerWithAddr(t, fmt.Sprintf("errHttp:123.%d.sock", os.Getpid()))
|
||||
eh := &errHandler{errCode: http.StatusServiceUnavailable}
|
||||
srv := httptest.NewUnstartedServer(eh)
|
||||
defer lHttp.Close()
|
||||
defer srv.Close()
|
||||
srv.Listener = lHttp
|
||||
go srv.Start()
|
||||
lHttpURL := integration.UrlScheme + "://" + lHttp.Addr().String()
|
||||
|
||||
cli := integration.MustNewHTTPClient(t, []string{lHttpURL, lHttpURL}, nil)
|
||||
kapi := client.NewKeysAPI(cli)
|
||||
// test error code
|
||||
for i, f := range noRetryList(kapi) {
|
||||
reqs := eh.reqs
|
||||
if err := f(); err == nil || !strings.Contains(err.Error(), "no leader") {
|
||||
t.Errorf("#%d: expected \"no leader\", got %v", i, err)
|
||||
}
|
||||
if eh.reqs != reqs+1 {
|
||||
t.Errorf("#%d: expected 1 request, got %d", i, eh.reqs-reqs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2RetryRefuse tests destructive api calls will retry if a connection is refused.
|
||||
func TestV2RetryRefuse(t *testing.T) {
|
||||
defer testutil.AfterTest(t)
|
||||
cl := integration.NewCluster(t, 1)
|
||||
cl.Launch(t)
|
||||
defer cl.Terminate(t)
|
||||
// test connection refused; expect no error failover
|
||||
cli := integration.MustNewHTTPClient(t, []string{integration.UrlScheme + "://refuseconn:123", cl.URL(0)}, nil)
|
||||
kapi := client.NewKeysAPI(cli)
|
||||
if _, err := kapi.Set(context.Background(), "/delkey", "def", nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i, f := range noRetryList(kapi) {
|
||||
if err := f(); err != nil {
|
||||
t.Errorf("#%d: unexpected retry failure (%v)", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type errHandler struct {
|
||||
errCode int
|
||||
reqs int
|
||||
}
|
||||
|
||||
func (eh *errHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
req.Body.Close()
|
||||
eh.reqs++
|
||||
w.WriteHeader(eh.errCode)
|
||||
}
|
||||
|
||||
func noRetryList(kapi client.KeysAPI) []func() error {
|
||||
return []func() error{
|
||||
func() error {
|
||||
opts := &client.SetOptions{PrevExist: client.PrevNoExist}
|
||||
_, err := kapi.Set(context.Background(), "/setkey", "bar", opts)
|
||||
return err
|
||||
},
|
||||
func() error {
|
||||
_, err := kapi.Delete(context.Background(), "/delkey", nil)
|
||||
return err
|
||||
},
|
||||
}
|
||||
}
|
20
client/integration/main_test.go
Normal file
20
client/integration/main_test.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/pkg/testutil"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
v := m.Run()
|
||||
if v == 0 && testutil.CheckLeakedGoroutine() {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(v)
|
||||
}
|
@ -337,7 +337,11 @@ func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions
|
||||
act.Dir = opts.Dir
|
||||
}
|
||||
|
||||
resp, body, err := k.client.Do(ctx, act)
|
||||
doCtx := ctx
|
||||
if act.PrevExist == PrevNoExist {
|
||||
doCtx = context.WithValue(doCtx, &oneShotCtxValue, &oneShotCtxValue)
|
||||
}
|
||||
resp, body, err := k.client.Do(doCtx, act)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -385,7 +389,8 @@ func (k *httpKeysAPI) Delete(ctx context.Context, key string, opts *DeleteOption
|
||||
act.Recursive = opts.Recursive
|
||||
}
|
||||
|
||||
resp, body, err := k.client.Do(ctx, act)
|
||||
doCtx := context.WithValue(ctx, &oneShotCtxValue, &oneShotCtxValue)
|
||||
resp, body, err := k.client.Do(doCtx, act)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ func ExampleKV_compact() {
|
||||
compRev := resp.Header.Revision // specify compact revision of your choice
|
||||
|
||||
ctx, cancel = context.WithTimeout(context.Background(), requestTimeout)
|
||||
err = cli.Compact(ctx, compRev)
|
||||
_, err = cli.Compact(ctx, compRev)
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -470,17 +470,17 @@ func TestKVCompactError(t *testing.T) {
|
||||
t.Fatalf("couldn't put 'foo' (%v)", err)
|
||||
}
|
||||
}
|
||||
err := kv.Compact(ctx, 6)
|
||||
_, err := kv.Compact(ctx, 6)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't compact 6 (%v)", err)
|
||||
}
|
||||
|
||||
err = kv.Compact(ctx, 6)
|
||||
_, err = kv.Compact(ctx, 6)
|
||||
if err != rpctypes.ErrCompacted {
|
||||
t.Fatalf("expected %v, got %v", rpctypes.ErrCompacted, err)
|
||||
}
|
||||
|
||||
err = kv.Compact(ctx, 100)
|
||||
_, err = kv.Compact(ctx, 100)
|
||||
if err != rpctypes.ErrFutureRev {
|
||||
t.Fatalf("expected %v, got %v", rpctypes.ErrFutureRev, err)
|
||||
}
|
||||
@ -501,11 +501,11 @@ func TestKVCompact(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
err := kv.Compact(ctx, 7)
|
||||
_, err := kv.Compact(ctx, 7)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't compact kv space (%v)", err)
|
||||
}
|
||||
err = kv.Compact(ctx, 7)
|
||||
_, err = kv.Compact(ctx, 7)
|
||||
if err == nil || err != rpctypes.ErrCompacted {
|
||||
t.Fatalf("error got %v, want %v", err, rpctypes.ErrCompacted)
|
||||
}
|
||||
@ -525,7 +525,7 @@ func TestKVCompact(t *testing.T) {
|
||||
t.Fatalf("wchan got %v, expected closed", wr)
|
||||
}
|
||||
|
||||
err = kv.Compact(ctx, 1000)
|
||||
_, err = kv.Compact(ctx, 1000)
|
||||
if err == nil || err != rpctypes.ErrFutureRev {
|
||||
t.Fatalf("error got %v, want %v", err, rpctypes.ErrFutureRev)
|
||||
}
|
||||
|
@ -15,7 +15,9 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -69,3 +71,55 @@ func TestMirrorSync(t *testing.T) {
|
||||
t.Fatal("failed to receive update in one second")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMirrorSyncBase(t *testing.T) {
|
||||
cluster := integration.NewClusterV3(nil, &integration.ClusterConfig{Size: 1})
|
||||
defer cluster.Terminate(nil)
|
||||
|
||||
cli := cluster.Client(0)
|
||||
ctx := context.TODO()
|
||||
|
||||
keyCh := make(chan string)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for key := range keyCh {
|
||||
if _, err := cli.Put(ctx, key, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
keyCh <- fmt.Sprintf("test%d", i)
|
||||
}
|
||||
|
||||
close(keyCh)
|
||||
wg.Wait()
|
||||
|
||||
syncer := mirror.NewSyncer(cli, "test", 0)
|
||||
respCh, errCh := syncer.SyncBase(ctx)
|
||||
|
||||
count := 0
|
||||
|
||||
for resp := range respCh {
|
||||
count = count + len(resp.Kvs)
|
||||
if !resp.More {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for err := range errCh {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if count != 2000 {
|
||||
t.Errorf("unexpected kv count: %d", count)
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ func TestWatchResumeCompacted(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := kv.Compact(context.TODO(), 3); err != nil {
|
||||
if _, err := kv.Compact(context.TODO(), 3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -414,7 +414,7 @@ func TestWatchCompactRevision(t *testing.T) {
|
||||
w := clientv3.NewWatcher(clus.RandClient())
|
||||
defer w.Close()
|
||||
|
||||
if err := kv.Compact(context.TODO(), 4); err != nil {
|
||||
if _, err := kv.Compact(context.TODO(), 4); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wch := w.Watch(context.Background(), "foo", clientv3.WithRev(2))
|
||||
|
@ -20,10 +20,11 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
PutResponse pb.PutResponse
|
||||
GetResponse pb.RangeResponse
|
||||
DeleteResponse pb.DeleteRangeResponse
|
||||
TxnResponse pb.TxnResponse
|
||||
CompactResponse pb.CompactionResponse
|
||||
PutResponse pb.PutResponse
|
||||
GetResponse pb.RangeResponse
|
||||
DeleteResponse pb.DeleteRangeResponse
|
||||
TxnResponse pb.TxnResponse
|
||||
)
|
||||
|
||||
type KV interface {
|
||||
@ -47,7 +48,7 @@ type KV interface {
|
||||
Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)
|
||||
|
||||
// Compact compacts etcd KV history before the given rev.
|
||||
Compact(ctx context.Context, rev int64, opts ...CompactOption) error
|
||||
Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)
|
||||
|
||||
// Do applies a single Op on KV without a transaction.
|
||||
// Do is useful when declaring operations to be issued at a later time
|
||||
@ -98,11 +99,12 @@ func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*Delete
|
||||
return r.del, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) error {
|
||||
if _, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest()); err != nil {
|
||||
return toErr(ctx, err)
|
||||
func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
|
||||
resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest())
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return nil
|
||||
return (*CompactResponse)(resp), err
|
||||
}
|
||||
|
||||
func (kv *kv) Txn(ctx context.Context) Txn {
|
||||
|
@ -78,7 +78,7 @@ func (s *syncer) SyncBase(ctx context.Context) (<-chan clientv3.GetResponse, cha
|
||||
// If len(s.prefix) != 0, we will sync key-value space with given prefix.
|
||||
// We then range from the prefix to the next prefix if exists. Or we will
|
||||
// range from the prefix to the end if the next prefix does not exists.
|
||||
opts = append(opts, clientv3.WithPrefix())
|
||||
opts = append(opts, clientv3.WithRange(clientv3.GetPrefixRangeEnd(s.prefix)))
|
||||
key = s.prefix
|
||||
}
|
||||
|
||||
|
@ -182,6 +182,12 @@ func WithSort(target SortTarget, order SortOrder) OpOption {
|
||||
}
|
||||
}
|
||||
|
||||
// GetPrefixRangeEnd gets the range end of the prefix.
|
||||
// 'Get(foo, WithPrefix())' is equal to 'Get(foo, WithRange(GetPrefixRangeEnd(foo))'.
|
||||
func GetPrefixRangeEnd(prefix string) string {
|
||||
return string(getPrefix([]byte(prefix)))
|
||||
}
|
||||
|
||||
func getPrefix(key []byte) []byte {
|
||||
end := make([]byte, len(key))
|
||||
copy(end, key)
|
||||
|
@ -505,6 +505,7 @@ func (w *watchGrpcStream) serveWatchClient(wc pb.Watch_WatchClient) {
|
||||
|
||||
// serveStream forwards watch responses from run() to the subscriber
|
||||
func (w *watchGrpcStream) serveStream(ws *watcherStream) {
|
||||
var closeErr error
|
||||
emptyWr := &WatchResponse{}
|
||||
wrs := []*WatchResponse{}
|
||||
resuming := false
|
||||
@ -569,13 +570,14 @@ func (w *watchGrpcStream) serveStream(ws *watcherStream) {
|
||||
}
|
||||
case <-w.donec:
|
||||
closing = true
|
||||
closeErr = w.closeErr
|
||||
case <-ws.initReq.ctx.Done():
|
||||
closing = true
|
||||
}
|
||||
}
|
||||
|
||||
// try to send off close error
|
||||
if w.closeErr != nil {
|
||||
if closeErr != nil {
|
||||
select {
|
||||
case ws.outc <- WatchResponse{closeErr: w.closeErr}:
|
||||
case <-w.donec:
|
||||
|
@ -497,25 +497,25 @@ ENDPOINT STATUS does not support protobuf encoded output.
|
||||
|
||||
```bash
|
||||
./etcdctl endpoint status
|
||||
127.0.0.1:2379, 8211f1d0f64f3269, 3.0.0-beta.0+git, 25 kB, false, 2, 63
|
||||
127.0.0.1:22379, 91bc3c398fb3c146, 3.0.0-beta.0+git, 25 kB, false, 2, 63
|
||||
127.0.0.1:32379, fd422379fda50e48, 3.0.0-beta.0+git, 25 kB, true, 2, 63
|
||||
127.0.0.1:2379, 8211f1d0f64f3269, 3.0.0, 25 kB, false, 2, 63
|
||||
127.0.0.1:22379, 91bc3c398fb3c146, 3.0.0, 25 kB, false, 2, 63
|
||||
127.0.0.1:32379, fd422379fda50e48, 3.0.0, 25 kB, true, 2, 63
|
||||
```
|
||||
|
||||
```bash
|
||||
./etcdctl -w json endpoint status
|
||||
[{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"2.3.0+git","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:22379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":10501334649042878790,"revision":2,"raft_term":2},"version":"2.3.0+git","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:32379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":18249187646912138824,"revision":2,"raft_term":2},"version":"2.3.0+git","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}]
|
||||
[{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:22379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":10501334649042878790,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:32379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":18249187646912138824,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}]
|
||||
```
|
||||
|
||||
```bash
|
||||
./etcdctl -w table endpoint status
|
||||
+-----------------+------------------+------------------+---------+-----------+-----------+------------+
|
||||
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
|
||||
+-----------------+------------------+------------------+---------+-----------+-----------+------------+
|
||||
| 127.0.0.1:2379 | 8211f1d0f64f3269 | 3.0.0-beta.0+git | 25 kB | false | 2 | 52 |
|
||||
| 127.0.0.1:22379 | 91bc3c398fb3c146 | 3.0.0-beta.0+git | 25 kB | false | 2 | 52 |
|
||||
| 127.0.0.1:32379 | fd422379fda50e48 | 3.0.0-beta.0+git | 25 kB | true | 2 | 52 |
|
||||
+-----------------+------------------+------------------+---------+-----------+-----------+------------+
|
||||
+-----------------+------------------+---------+---------+-----------+-----------+------------+
|
||||
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
|
||||
+-----------------+------------------+---------+---------+-----------+-----------+------------+
|
||||
| 127.0.0.1:2379 | 8211f1d0f64f3269 | 3.0.0 | 25 kB | false | 2 | 52 |
|
||||
| 127.0.0.1:22379 | 91bc3c398fb3c146 | 3.0.0 | 25 kB | false | 2 | 52 |
|
||||
| 127.0.0.1:32379 | fd422379fda50e48 | 3.0.0 | 25 kB | true | 2 | 52 |
|
||||
+-----------------+------------------+---------+---------+-----------+-----------+------------+
|
||||
```
|
||||
|
||||
### LOCK \<lockname\>
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
func NewAlarmCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "alarm <subcommand>",
|
||||
Short: "alarm related command",
|
||||
Short: "Alarm related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(NewAlarmDisarmCommand())
|
||||
@ -37,7 +37,7 @@ func NewAlarmCommand() *cobra.Command {
|
||||
func NewAlarmDisarmCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "disarm",
|
||||
Short: "disarm all alarms",
|
||||
Short: "Disarms all alarms",
|
||||
Run: alarmDisarmCommandFunc,
|
||||
}
|
||||
return &cmd
|
||||
@ -60,7 +60,7 @@ func alarmDisarmCommandFunc(cmd *cobra.Command, args []string) {
|
||||
func NewAlarmListCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list all alarms",
|
||||
Short: "Lists all alarms",
|
||||
Run: alarmListCommandFunc,
|
||||
}
|
||||
return &cmd
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
func NewAuthCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "auth <enable or disable>",
|
||||
Short: "Enable or disable authentication.",
|
||||
Short: "Enable or disable authentication",
|
||||
}
|
||||
|
||||
ac.AddCommand(newAuthEnableCommand())
|
||||
@ -36,7 +36,7 @@ func NewAuthCommand() *cobra.Command {
|
||||
func newAuthEnableCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "enable",
|
||||
Short: "enable authentication",
|
||||
Short: "Enables authentication",
|
||||
Run: authEnableCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -60,7 +60,7 @@ func authEnableCommandFunc(cmd *cobra.Command, args []string) {
|
||||
func newAuthDisableCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "disable",
|
||||
Short: "disable authentication",
|
||||
Short: "Disables authentication",
|
||||
Run: authDisableCommandFunc,
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ var compactPhysical bool
|
||||
func NewCompactionCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "compaction <revision>",
|
||||
Short: "Compaction compacts the event history in etcd.",
|
||||
Short: "Compacts the event history in etcd",
|
||||
Run: compactionCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVar(&compactPhysical, "physical", false, "'true' to wait for compaction to physically remove all old revisions.")
|
||||
cmd.Flags().BoolVar(&compactPhysical, "physical", false, "'true' to wait for compaction to physically remove all old revisions")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ func compactionCommandFunc(cmd *cobra.Command, args []string) {
|
||||
|
||||
c := mustClientFromCmd(cmd)
|
||||
ctx, cancel := commandCtx(cmd)
|
||||
cerr := c.Compact(ctx, rev, opts...)
|
||||
_, cerr := c.Compact(ctx, rev, opts...)
|
||||
cancel()
|
||||
if cerr != nil {
|
||||
ExitWithError(ExitError, cerr)
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
func NewDefragCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "defrag",
|
||||
Short: "defrag defragments the storage of the etcd members with given endpoints.",
|
||||
Short: "Defragments the storage of the etcd members with given endpoints",
|
||||
Run: defragCommandFunc,
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ var (
|
||||
func NewDelCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "del [options] <key> [range_end]",
|
||||
Short: "Removes the specified key or range of keys [key, range_end).",
|
||||
Short: "Removes the specified key or range of keys [key, range_end)",
|
||||
Run: delCommandFunc,
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ var (
|
||||
func NewElectCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "elect <election-name> [proposal]",
|
||||
Short: "elect observes and participates in leader election",
|
||||
Short: "Observes and participates in leader election",
|
||||
Run: electCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVarP(&electListen, "listen", "l", false, "observation mode")
|
||||
|
@ -28,8 +28,8 @@ import (
|
||||
// NewEndpointCommand returns the cobra command for "endpoint".
|
||||
func NewEndpointCommand() *cobra.Command {
|
||||
ec := &cobra.Command{
|
||||
Use: "endpoint",
|
||||
Short: "endpoint is used to check endpoints.",
|
||||
Use: "endpoint <subcommand>",
|
||||
Short: "Endpoint related commands",
|
||||
}
|
||||
|
||||
ec.AddCommand(newEpHealthCommand())
|
||||
@ -41,7 +41,7 @@ func NewEndpointCommand() *cobra.Command {
|
||||
func newEpHealthCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "health",
|
||||
Short: "health checks the healthiness of endpoints specified in `--endpoints` flag",
|
||||
Short: "Checks the healthiness of endpoints specified in `--endpoints` flag",
|
||||
Run: epHealthCommandFunc,
|
||||
}
|
||||
return cmd
|
||||
@ -50,7 +50,7 @@ func newEpHealthCommand() *cobra.Command {
|
||||
func newEpStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "status prints out the status of endpoints specified in `--endpoints` flag",
|
||||
Short: "Prints out the status of endpoints specified in `--endpoints` flag",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
|
||||
The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index.
|
||||
`,
|
||||
|
@ -37,18 +37,18 @@ var (
|
||||
func NewGetCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [options] <key> [range_end]",
|
||||
Short: "Get gets the key or a range of keys.",
|
||||
Short: "Gets the key or a range of keys",
|
||||
Run: getCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&getConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)")
|
||||
cmd.Flags().StringVar(&getSortOrder, "order", "", "order of results; ASCEND or DESCEND")
|
||||
cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
|
||||
cmd.Flags().Int64Var(&getLimit, "limit", 0, "maximum number of results")
|
||||
cmd.Flags().BoolVar(&getPrefix, "prefix", false, "get keys with matching prefix")
|
||||
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "get keys that are greater than or equal to the given key")
|
||||
cmd.Flags().Int64Var(&getRev, "rev", 0, "specify the kv revision")
|
||||
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "get only the keys")
|
||||
cmd.Flags().StringVar(&getSortOrder, "order", "", "Order of results; ASCEND or DESCEND")
|
||||
cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "Sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
|
||||
cmd.Flags().Int64Var(&getLimit, "limit", 0, "Maximum number of results")
|
||||
cmd.Flags().BoolVar(&getPrefix, "prefix", false, "Get keys with matching prefix")
|
||||
cmd.Flags().BoolVar(&getFromKey, "from-key", false, "Get keys that are greater than or equal to the given key")
|
||||
cmd.Flags().Int64Var(&getRev, "rev", 0, "Specify the kv revision")
|
||||
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,8 @@ import (
|
||||
// NewLeaseCommand returns the cobra command for "lease".
|
||||
func NewLeaseCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "lease",
|
||||
Short: "lease is used to manage leases.",
|
||||
Use: "lease <subcommand>",
|
||||
Short: "Lease related commands",
|
||||
}
|
||||
|
||||
lc.AddCommand(NewLeaseGrantCommand())
|
||||
@ -41,7 +41,7 @@ func NewLeaseCommand() *cobra.Command {
|
||||
func NewLeaseGrantCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "grant <ttl>",
|
||||
Short: "grant is used to create leases.",
|
||||
Short: "Creates leases",
|
||||
|
||||
Run: leaseGrantCommandFunc,
|
||||
}
|
||||
@ -73,7 +73,7 @@ func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
|
||||
func NewLeaseRevokeCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "revoke <leaseID>",
|
||||
Short: "revoke is used to revoke leases.",
|
||||
Short: "Revokes leases",
|
||||
|
||||
Run: leaseRevokeCommandFunc,
|
||||
}
|
||||
@ -105,7 +105,7 @@ func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
|
||||
func NewLeaseKeepAliveCommand() *cobra.Command {
|
||||
lc := &cobra.Command{
|
||||
Use: "keep-alive <leaseID>",
|
||||
Short: "keep-alive is used to keep leases alive.",
|
||||
Short: "Keeps leases alive (renew)",
|
||||
|
||||
Run: leaseKeepAliveCommandFunc,
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
func NewLockCommand() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "lock <lockname>",
|
||||
Short: "lock acquires a named lock",
|
||||
Short: "Acquires a named lock",
|
||||
Run: lockCommandFunc,
|
||||
}
|
||||
return c
|
||||
|
@ -40,17 +40,17 @@ var (
|
||||
func NewMakeMirrorCommand() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "make-mirror [options] <destination>",
|
||||
Short: "make-mirror makes a mirror at the destination etcd cluster",
|
||||
Short: "Makes a mirror at the destination etcd cluster",
|
||||
Run: makeMirrorCommandFunc,
|
||||
}
|
||||
|
||||
c.Flags().StringVar(&mmprefix, "prefix", "", "the key-value prefix to mirror")
|
||||
c.Flags().StringVar(&mmprefix, "prefix", "", "Key-value prefix to mirror")
|
||||
// TODO: add dest-prefix to mirror a prefix to a different prefix in the destination cluster?
|
||||
c.Flags().StringVar(&mmcert, "dest-cert", "", "identify secure client using this TLS certificate file for the destination cluster")
|
||||
c.Flags().StringVar(&mmkey, "dest-key", "", "identify secure client using this TLS key file")
|
||||
c.Flags().StringVar(&mmcacert, "dest-cacert", "", "verify certificates of TLS enabled secure servers using this CA bundle")
|
||||
c.Flags().StringVar(&mmcert, "dest-cert", "", "Identify secure client using this TLS certificate file for the destination cluster")
|
||||
c.Flags().StringVar(&mmkey, "dest-key", "", "Identify secure client using this TLS key file")
|
||||
c.Flags().StringVar(&mmcacert, "dest-cacert", "", "Verify certificates of TLS enabled secure servers using this CA bundle")
|
||||
// TODO: secure by default when etcd enables secure gRPC by default.
|
||||
c.Flags().BoolVar(&mminsecureTr, "dest-insecure-transport", true, "disable transport security for client connections")
|
||||
c.Flags().BoolVar(&mminsecureTr, "dest-insecure-transport", true, "Disable transport security for client connections")
|
||||
|
||||
return c
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ var memberPeerURLs string
|
||||
// NewMemberCommand returns the cobra command for "member".
|
||||
func NewMemberCommand() *cobra.Command {
|
||||
mc := &cobra.Command{
|
||||
Use: "member",
|
||||
Short: "member is used to manage membership in an etcd cluster.",
|
||||
Use: "member <subcommand>",
|
||||
Short: "Membership related commands",
|
||||
}
|
||||
|
||||
mc.AddCommand(NewMemberAddCommand())
|
||||
@ -43,7 +43,7 @@ func NewMemberCommand() *cobra.Command {
|
||||
func NewMemberAddCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "add <memberName>",
|
||||
Short: "add is used to add a member into the cluster",
|
||||
Short: "Adds a member into the cluster",
|
||||
|
||||
Run: memberAddCommandFunc,
|
||||
}
|
||||
@ -57,7 +57,7 @@ func NewMemberAddCommand() *cobra.Command {
|
||||
func NewMemberRemoveCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "remove <memberID>",
|
||||
Short: "remove is used to remove a member from the cluster",
|
||||
Short: "Removes a member from the cluster",
|
||||
|
||||
Run: memberRemoveCommandFunc,
|
||||
}
|
||||
@ -69,7 +69,7 @@ func NewMemberRemoveCommand() *cobra.Command {
|
||||
func NewMemberUpdateCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "update <memberID>",
|
||||
Short: "update is used to update a member in the cluster",
|
||||
Short: "Updates a member in the cluster",
|
||||
|
||||
Run: memberUpdateCommandFunc,
|
||||
}
|
||||
@ -83,7 +83,7 @@ func NewMemberUpdateCommand() *cobra.Command {
|
||||
func NewMemberListCommand() *cobra.Command {
|
||||
cc := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list is used to list all members in the cluster",
|
||||
Short: "Lists all members in the cluster",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
|
||||
The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs.
|
||||
`,
|
||||
|
@ -51,13 +51,13 @@ var (
|
||||
func NewMigrateCommand() *cobra.Command {
|
||||
mc := &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "migrates keys in a v2 store to a mvcc store",
|
||||
Short: "Migrates keys in a v2 store to a mvcc store",
|
||||
Run: migrateCommandFunc,
|
||||
}
|
||||
|
||||
mc.Flags().StringVar(&migrateDatadir, "data-dir", "", "Path to the data directory.")
|
||||
mc.Flags().StringVar(&migrateWALdir, "wal-dir", "", "Path to the WAL directory.")
|
||||
mc.Flags().StringVar(&migrateTransformer, "transformer", "", "Path to the user-provided transformer program.")
|
||||
mc.Flags().StringVar(&migrateDatadir, "data-dir", "", "Path to the data directory")
|
||||
mc.Flags().StringVar(&migrateWALdir, "wal-dir", "", "Path to the WAL directory")
|
||||
mc.Flags().StringVar(&migrateTransformer, "transformer", "", "Path to the user-provided transformer program")
|
||||
return mc
|
||||
}
|
||||
|
||||
|
@ -31,9 +31,9 @@ var (
|
||||
func NewPutCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "put [options] <key> <value> (<value> can also be given from stdin)",
|
||||
Short: "Put puts the given key into the store.",
|
||||
Short: "Puts the given key into the store",
|
||||
Long: `
|
||||
Put puts the given key into the store.
|
||||
Puts the given key into the store.
|
||||
|
||||
When <value> begins with '-', <value> is interpreted as a flag.
|
||||
Insert '--' for workaround:
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
func NewRoleCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "role <subcommand>",
|
||||
Short: "role related command",
|
||||
Short: "Role related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(newRoleAddCommand())
|
||||
@ -42,7 +42,7 @@ func NewRoleCommand() *cobra.Command {
|
||||
func newRoleAddCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "add <role name>",
|
||||
Short: "add a new role",
|
||||
Short: "Adds a new role",
|
||||
Run: roleAddCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -50,7 +50,7 @@ func newRoleAddCommand() *cobra.Command {
|
||||
func newRoleDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete <role name>",
|
||||
Short: "delete a role",
|
||||
Short: "Deletes a role",
|
||||
Run: roleDeleteCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,7 @@ func newRoleDeleteCommand() *cobra.Command {
|
||||
func newRoleGetCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "get <role name>",
|
||||
Short: "get detailed information of a role",
|
||||
Short: "Gets detailed information of a role",
|
||||
Run: roleGetCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ func newRoleGetCommand() *cobra.Command {
|
||||
func newRoleListCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list up all roles",
|
||||
Short: "Lists all roles",
|
||||
Run: roleListCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -74,7 +74,7 @@ func newRoleListCommand() *cobra.Command {
|
||||
func newRoleGrantPermissionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "grant-permission <role name> <permission type> <key> [endkey]",
|
||||
Short: "grant a key to a role",
|
||||
Short: "Grants a key to a role",
|
||||
Run: roleGrantPermissionCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@ func newRoleGrantPermissionCommand() *cobra.Command {
|
||||
func newRoleRevokePermissionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "revoke-permission <role name> <key> [endkey]",
|
||||
Short: "revoke a key from a role",
|
||||
Short: "Revokes a key from a role",
|
||||
Run: roleRevokePermissionCommandFunc,
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ var (
|
||||
// NewSnapshotCommand returns the cobra command for "snapshot".
|
||||
func NewSnapshotCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "snapshot",
|
||||
Short: "snapshot manages etcd node snapshots.",
|
||||
Use: "snapshot <subcommand>",
|
||||
Short: "Manages etcd node snapshots",
|
||||
}
|
||||
cmd.AddCommand(NewSnapshotSaveCommand())
|
||||
cmd.AddCommand(NewSnapshotRestoreCommand())
|
||||
@ -70,7 +70,7 @@ func NewSnapshotCommand() *cobra.Command {
|
||||
func NewSnapshotSaveCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "save <filename>",
|
||||
Short: "save stores an etcd node backend snapshot to a given file.",
|
||||
Short: "Stores an etcd node backend snapshot to a given file",
|
||||
Run: snapshotSaveCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -78,7 +78,7 @@ func NewSnapshotSaveCommand() *cobra.Command {
|
||||
func newSnapshotStatusCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "status <filename>",
|
||||
Short: "status gets backend snapshot status of a given file.",
|
||||
Short: "Gets backend snapshot status of a given file",
|
||||
Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
|
||||
The items in the lists are hash, revision, total keys, total size.
|
||||
`,
|
||||
@ -89,15 +89,15 @@ The items in the lists are hash, revision, total keys, total size.
|
||||
func NewSnapshotRestoreCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "restore <filename>",
|
||||
Short: "restore an etcd member snapshot to an etcd directory",
|
||||
Short: "Restores an etcd member snapshot to an etcd directory",
|
||||
Run: snapshotRestoreCommandFunc,
|
||||
}
|
||||
cmd.Flags().StringVar(&restoreDataDir, "data-dir", "", "Path to the data directory.")
|
||||
cmd.Flags().StringVar(&restoreCluster, "initial-cluster", initialClusterFromName(defaultName), "Initial cluster configuration for restore bootstrap.")
|
||||
cmd.Flags().StringVar(&restoreClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during restore bootstrap.")
|
||||
cmd.Flags().StringVar(&restorePeerURLs, "initial-advertise-peer-urls", defaultInitialAdvertisePeerURLs, "List of this member's peer URLs to advertise to the rest of the cluster.")
|
||||
cmd.Flags().StringVar(&restoreName, "name", defaultName, "Human-readable name for this member.")
|
||||
cmd.Flags().BoolVar(&skipHashCheck, "skip-hash-check", false, "Ignore snapshot integrity hash value (required if copied from data directory).")
|
||||
cmd.Flags().StringVar(&restoreDataDir, "data-dir", "", "Path to the data directory")
|
||||
cmd.Flags().StringVar(&restoreCluster, "initial-cluster", initialClusterFromName(defaultName), "Initial cluster configuration for restore bootstrap")
|
||||
cmd.Flags().StringVar(&restoreClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during restore bootstrap")
|
||||
cmd.Flags().StringVar(&restorePeerURLs, "initial-advertise-peer-urls", defaultInitialAdvertisePeerURLs, "List of this member's peer URLs to advertise to the rest of the cluster")
|
||||
cmd.Flags().StringVar(&restoreName, "name", defaultName, "Human-readable name for this member")
|
||||
cmd.Flags().BoolVar(&skipHashCheck, "skip-hash-check", false, "Ignore snapshot integrity hash value (required if copied from data directory)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -34,10 +34,10 @@ var (
|
||||
func NewTxnCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "txn [options]",
|
||||
Short: "Txn processes all the requests in one transaction.",
|
||||
Short: "Txn processes all the requests in one transaction",
|
||||
Run: txnCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVarP(&txnInteractive, "interactive", "i", false, "input transaction in interactive mode")
|
||||
cmd.Flags().BoolVarP(&txnInteractive, "interactive", "i", false, "Input transaction in interactive mode")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ var (
|
||||
func NewUserCommand() *cobra.Command {
|
||||
ac := &cobra.Command{
|
||||
Use: "user <subcommand>",
|
||||
Short: "user related command",
|
||||
Short: "User related commands",
|
||||
}
|
||||
|
||||
ac.AddCommand(newUserAddCommand())
|
||||
@ -52,11 +52,11 @@ var (
|
||||
func newUserAddCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "add <user name>",
|
||||
Short: "add a new user",
|
||||
Short: "Adds a new user",
|
||||
Run: userAddCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "read password from stdin instead of interactive terminal")
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "Read password from stdin instead of interactive terminal")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
@ -64,7 +64,7 @@ func newUserAddCommand() *cobra.Command {
|
||||
func newUserDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete <user name>",
|
||||
Short: "delete a user",
|
||||
Short: "Deletes a user",
|
||||
Run: userDeleteCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -72,11 +72,11 @@ func newUserDeleteCommand() *cobra.Command {
|
||||
func newUserGetCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "get <user name>",
|
||||
Short: "get detailed information of a user",
|
||||
Short: "Gets detailed information of a user",
|
||||
Run: userGetCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&userShowDetail, "detail", false, "show permissions of roles granted to the user")
|
||||
cmd.Flags().BoolVar(&userShowDetail, "detail", false, "Show permissions of roles granted to the user")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
@ -84,7 +84,7 @@ func newUserGetCommand() *cobra.Command {
|
||||
func newUserListCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list up all users",
|
||||
Short: "Lists all users",
|
||||
Run: userListCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -92,11 +92,11 @@ func newUserListCommand() *cobra.Command {
|
||||
func newUserChangePasswordCommand() *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "passwd <user name>",
|
||||
Short: "change password of user",
|
||||
Short: "Changes password of user",
|
||||
Run: userChangePasswordCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "if true, read password from stdin instead of interactive terminal")
|
||||
cmd.Flags().BoolVar(&passwordInteractive, "interactive", true, "If true, read password from stdin instead of interactive terminal")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
@ -104,7 +104,7 @@ func newUserChangePasswordCommand() *cobra.Command {
|
||||
func newUserGrantRoleCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "grant-role <user name> <role name>",
|
||||
Short: "grant a role to a user",
|
||||
Short: "Grants a role to a user",
|
||||
Run: userGrantRoleCommandFunc,
|
||||
}
|
||||
}
|
||||
@ -112,7 +112,7 @@ func newUserGrantRoleCommand() *cobra.Command {
|
||||
func newUserRevokeRoleCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "revoke-role <user name> <role name>",
|
||||
Short: "revoke a role from a user",
|
||||
Short: "Revokes a role from a user",
|
||||
Run: userRevokeRoleCommandFunc,
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version of etcdctl.",
|
||||
Short: "Prints the version of etcdctl",
|
||||
Run: versionCommandFunc,
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,13 @@ var (
|
||||
func NewWatchCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "watch [options] [key or prefix] [range_end]",
|
||||
Short: "Watch watches events stream on keys or prefixes.",
|
||||
Short: "Watches events stream on keys or prefixes",
|
||||
Run: watchCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVarP(&watchInteractive, "interactive", "i", false, "interactive mode")
|
||||
cmd.Flags().BoolVar(&watchPrefix, "prefix", false, "watch on a prefix if prefix is set")
|
||||
cmd.Flags().Int64Var(&watchRev, "rev", 0, "revision to start watching")
|
||||
cmd.Flags().BoolVarP(&watchInteractive, "interactive", "i", false, "Interactive mode")
|
||||
cmd.Flags().BoolVar(&watchPrefix, "prefix", false, "Watch on a prefix if prefix is set")
|
||||
cmd.Flags().Int64Var(&watchRev, "rev", 0, "Revision to start watching")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/etcdserver"
|
||||
"github.com/coreos/etcd/version"
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
)
|
||||
@ -71,7 +72,7 @@ func runCapabilityLoop(s *etcdserver.EtcdServer) {
|
||||
enableMapMu.Lock()
|
||||
enabledMap = capabilityMaps[pv.String()]
|
||||
enableMapMu.Unlock()
|
||||
plog.Infof("enabled capabilities for version %s", pv)
|
||||
plog.Infof("enabled capabilities for version %s", version.Cluster(pv.String()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,11 +86,11 @@ type serverWatchStream struct {
|
||||
watchStream mvcc.WatchStream
|
||||
ctrlStream chan *pb.WatchResponse
|
||||
|
||||
// mu protects progress, prevKV
|
||||
mu sync.Mutex
|
||||
// progress tracks the watchID that stream might need to send
|
||||
// progress to.
|
||||
progress map[mvcc.WatchID]bool
|
||||
// mu protects progress
|
||||
mu sync.Mutex
|
||||
|
||||
// closec indicates the stream is closed.
|
||||
closec chan struct{}
|
||||
@ -171,7 +171,9 @@ func (sws *serverWatchStream) recvLoop() error {
|
||||
}
|
||||
id := sws.watchStream.Watch(creq.Key, creq.RangeEnd, rev)
|
||||
if id != -1 && creq.ProgressNotify {
|
||||
sws.mu.Lock()
|
||||
sws.progress[id] = true
|
||||
sws.mu.Unlock()
|
||||
}
|
||||
wr := &pb.WatchResponse{
|
||||
Header: sws.newResponseHeader(wsrev),
|
||||
@ -199,9 +201,11 @@ func (sws *serverWatchStream) recvLoop() error {
|
||||
sws.mu.Unlock()
|
||||
}
|
||||
}
|
||||
// TODO: do we need to return error back to client?
|
||||
default:
|
||||
panic("not implemented")
|
||||
// we probably should not shutdown the entire stream when
|
||||
// receive an valid command.
|
||||
// so just do nothing instead.
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,12 +300,14 @@ func (sws *serverWatchStream) sendLoop() {
|
||||
delete(pending, wid)
|
||||
}
|
||||
case <-progressTicker.C:
|
||||
sws.mu.Lock()
|
||||
for id, ok := range sws.progress {
|
||||
if ok {
|
||||
sws.watchStream.RequestProgress(id)
|
||||
}
|
||||
sws.progress[id] = true
|
||||
}
|
||||
sws.mu.Unlock()
|
||||
case <-sws.closec:
|
||||
return
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ func NewServer(cfg *ServerConfig) (srv *EtcdServer, err error) {
|
||||
cl.SetStore(st)
|
||||
cl.SetBackend(be)
|
||||
cl.Recover()
|
||||
if cl.Version() != nil && cl.Version().LessThan(semver.Version{Major: 3}) && !beExist {
|
||||
if cl.Version() != nil && !cl.Version().LessThan(semver.Version{Major: 3}) && !beExist {
|
||||
os.RemoveAll(bepath)
|
||||
return nil, fmt.Errorf("database file (%v) of the backend is missing", bepath)
|
||||
}
|
||||
@ -1170,8 +1170,7 @@ func (s *EtcdServer) snapshot(snapi uint64, confState raftpb.ConfState) {
|
||||
}
|
||||
plog.Panicf("unexpected create snapshot error %v", err)
|
||||
}
|
||||
// commit v3 storage because WAL file before snapshot index
|
||||
// could be removed after SaveSnap.
|
||||
// commit kv to write metadata (for example: consistent index) to disk.
|
||||
s.KV().Commit()
|
||||
// SaveSnap saves the snapshot and releases the locked wal files
|
||||
// to the snapshot index.
|
||||
|
@ -39,6 +39,8 @@ func (s *EtcdServer) createMergedSnapshotMessage(m raftpb.Message, snapi uint64,
|
||||
plog.Panicf("store save should never fail: %v", err)
|
||||
}
|
||||
|
||||
// commit kv to write metadata(for example: consistent index).
|
||||
s.KV().Commit()
|
||||
dbsnap := s.be.Snapshot()
|
||||
// get a snapshot of v3 KV as readCloser
|
||||
rc := newSnapshotReaderCloser(dbsnap)
|
||||
|
@ -8,6 +8,7 @@ all: cfssl ca req
|
||||
cfssl:
|
||||
go get -u -tags nopkcs11 github.com/cloudflare/cfssl/cmd/cfssl
|
||||
go get -u github.com/cloudflare/cfssl/cmd/cfssljson
|
||||
go get -u github.com/mattn/goreman
|
||||
|
||||
ca:
|
||||
mkdir -p certs
|
||||
|
@ -54,8 +54,8 @@ const (
|
||||
requestTimeout = 20 * time.Second
|
||||
|
||||
basePort = 21000
|
||||
urlScheme = "unix"
|
||||
urlSchemeTLS = "unixs"
|
||||
UrlScheme = "unix"
|
||||
UrlSchemeTLS = "unixs"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -96,9 +96,9 @@ func init() {
|
||||
|
||||
func schemeFromTLSInfo(tls *transport.TLSInfo) string {
|
||||
if tls == nil {
|
||||
return urlScheme
|
||||
return UrlScheme
|
||||
}
|
||||
return urlSchemeTLS
|
||||
return UrlSchemeTLS
|
||||
}
|
||||
|
||||
func (c *cluster) fillClusterForMembers() error {
|
||||
@ -257,7 +257,7 @@ func (c *cluster) addMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func (c *cluster) addMemberByURL(t *testing.T, clientURL, peerURL string) error {
|
||||
cc := mustNewHTTPClient(t, []string{clientURL}, c.cfg.ClientTLS)
|
||||
cc := MustNewHTTPClient(t, []string{clientURL}, c.cfg.ClientTLS)
|
||||
ma := client.NewMembersAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if _, err := ma.Add(ctx, peerURL); err != nil {
|
||||
@ -277,7 +277,7 @@ func (c *cluster) AddMember(t *testing.T) {
|
||||
|
||||
func (c *cluster) RemoveMember(t *testing.T, id uint64) {
|
||||
// send remove request to the cluster
|
||||
cc := mustNewHTTPClient(t, c.URLs(), c.cfg.ClientTLS)
|
||||
cc := MustNewHTTPClient(t, c.URLs(), c.cfg.ClientTLS)
|
||||
ma := client.NewMembersAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if err := ma.Remove(ctx, types.ID(id).String()); err != nil {
|
||||
@ -312,7 +312,7 @@ func (c *cluster) Terminate(t *testing.T) {
|
||||
|
||||
func (c *cluster) waitMembersMatch(t *testing.T, membs []client.Member) {
|
||||
for _, u := range c.URLs() {
|
||||
cc := mustNewHTTPClient(t, []string{u}, c.cfg.ClientTLS)
|
||||
cc := MustNewHTTPClient(t, []string{u}, c.cfg.ClientTLS)
|
||||
ma := client.NewMembersAPI(cc)
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
@ -391,10 +391,10 @@ func isMembersEqual(membs []client.Member, wmembs []client.Member) bool {
|
||||
func newLocalListener(t *testing.T) net.Listener {
|
||||
c := atomic.AddInt64(&localListenCount, 1)
|
||||
addr := fmt.Sprintf("127.0.0.1:%d.%d.sock", c+basePort, os.Getpid())
|
||||
return newListenerWithAddr(t, addr)
|
||||
return NewListenerWithAddr(t, addr)
|
||||
}
|
||||
|
||||
func newListenerWithAddr(t *testing.T, addr string) net.Listener {
|
||||
func NewListenerWithAddr(t *testing.T, addr string) net.Listener {
|
||||
l, err := transport.NewUnixListener(addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -614,7 +614,7 @@ func (m *member) Launch() error {
|
||||
}
|
||||
|
||||
func (m *member) WaitOK(t *testing.T) {
|
||||
cc := mustNewHTTPClient(t, []string{m.URL()}, m.ClientTLSInfo)
|
||||
cc := MustNewHTTPClient(t, []string{m.URL()}, m.ClientTLSInfo)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
@ -678,12 +678,12 @@ func (m *member) Restart(t *testing.T) error {
|
||||
plog.Printf("restarting %s (%s)", m.Name, m.grpcAddr)
|
||||
newPeerListeners := make([]net.Listener, 0)
|
||||
for _, ln := range m.PeerListeners {
|
||||
newPeerListeners = append(newPeerListeners, newListenerWithAddr(t, ln.Addr().String()))
|
||||
newPeerListeners = append(newPeerListeners, NewListenerWithAddr(t, ln.Addr().String()))
|
||||
}
|
||||
m.PeerListeners = newPeerListeners
|
||||
newClientListeners := make([]net.Listener, 0)
|
||||
for _, ln := range m.ClientListeners {
|
||||
newClientListeners = append(newClientListeners, newListenerWithAddr(t, ln.Addr().String()))
|
||||
newClientListeners = append(newClientListeners, NewListenerWithAddr(t, ln.Addr().String()))
|
||||
}
|
||||
m.ClientListeners = newClientListeners
|
||||
|
||||
@ -708,7 +708,7 @@ func (m *member) Terminate(t *testing.T) {
|
||||
plog.Printf("terminated %s (%s)", m.Name, m.grpcAddr)
|
||||
}
|
||||
|
||||
func mustNewHTTPClient(t *testing.T, eps []string, tls *transport.TLSInfo) client.Client {
|
||||
func MustNewHTTPClient(t *testing.T, eps []string, tls *transport.TLSInfo) client.Client {
|
||||
cfgtls := transport.TLSInfo{}
|
||||
if tls != nil {
|
||||
cfgtls = *tls
|
||||
|
@ -67,7 +67,7 @@ func testClusterUsingDiscovery(t *testing.T, size int) {
|
||||
dc.Launch(t)
|
||||
defer dc.Terminate(t)
|
||||
// init discovery token space
|
||||
dcc := mustNewHTTPClient(t, dc.URLs(), nil)
|
||||
dcc := MustNewHTTPClient(t, dc.URLs(), nil)
|
||||
dkapi := client.NewKeysAPI(dcc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if _, err := dkapi.Create(ctx, "/_config/size", fmt.Sprintf("%d", size)); err != nil {
|
||||
@ -90,7 +90,7 @@ func TestTLSClusterOf3UsingDiscovery(t *testing.T) {
|
||||
dc.Launch(t)
|
||||
defer dc.Terminate(t)
|
||||
// init discovery token space
|
||||
dcc := mustNewHTTPClient(t, dc.URLs(), nil)
|
||||
dcc := MustNewHTTPClient(t, dc.URLs(), nil)
|
||||
dkapi := client.NewKeysAPI(dcc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if _, err := dkapi.Create(ctx, "/_config/size", fmt.Sprintf("%d", 3)); err != nil {
|
||||
@ -157,7 +157,7 @@ func testDecreaseClusterSize(t *testing.T, size int) {
|
||||
func TestForceNewCluster(t *testing.T) {
|
||||
c := NewCluster(t, 3)
|
||||
c.Launch(t)
|
||||
cc := mustNewHTTPClient(t, []string{c.Members[0].URL()}, nil)
|
||||
cc := MustNewHTTPClient(t, []string{c.Members[0].URL()}, nil)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
resp, err := kapi.Create(ctx, "/foo", "bar")
|
||||
@ -184,7 +184,7 @@ func TestForceNewCluster(t *testing.T) {
|
||||
c.waitLeader(t, c.Members[:1])
|
||||
|
||||
// use new http client to init new connection
|
||||
cc = mustNewHTTPClient(t, []string{c.Members[0].URL()}, nil)
|
||||
cc = MustNewHTTPClient(t, []string{c.Members[0].URL()}, nil)
|
||||
kapi = client.NewKeysAPI(cc)
|
||||
// ensure force restart keep the old data, and new cluster can make progress
|
||||
ctx, cancel = context.WithTimeout(context.Background(), requestTimeout)
|
||||
@ -273,7 +273,7 @@ func TestIssue2904(t *testing.T) {
|
||||
c.Members[1].Stop(t)
|
||||
|
||||
// send remove member-1 request to the cluster.
|
||||
cc := mustNewHTTPClient(t, c.URLs(), nil)
|
||||
cc := MustNewHTTPClient(t, c.URLs(), nil)
|
||||
ma := client.NewMembersAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
// the proposal is not committed because member 1 is stopped, but the
|
||||
@ -337,7 +337,7 @@ func TestIssue3699(t *testing.T) {
|
||||
c.waitLeader(t, c.Members)
|
||||
|
||||
// try to participate in cluster
|
||||
cc := mustNewHTTPClient(t, []string{c.URL(0)}, c.cfg.ClientTLS)
|
||||
cc := MustNewHTTPClient(t, []string{c.URL(0)}, c.cfg.ClientTLS)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if _, err := kapi.Set(ctx, "/foo", "bar", nil); err != nil {
|
||||
@ -350,7 +350,7 @@ func TestIssue3699(t *testing.T) {
|
||||
// a random key first, and check the new key could be got from all client urls
|
||||
// of the cluster.
|
||||
func clusterMustProgress(t *testing.T, membs []*member) {
|
||||
cc := mustNewHTTPClient(t, []string{membs[0].URL()}, nil)
|
||||
cc := MustNewHTTPClient(t, []string{membs[0].URL()}, nil)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
key := fmt.Sprintf("foo%d", rand.Int())
|
||||
@ -362,7 +362,7 @@ func clusterMustProgress(t *testing.T, membs []*member) {
|
||||
|
||||
for i, m := range membs {
|
||||
u := m.URL()
|
||||
mcc := mustNewHTTPClient(t, []string{u}, nil)
|
||||
mcc := MustNewHTTPClient(t, []string{u}, nil)
|
||||
mkapi := client.NewKeysAPI(mcc)
|
||||
mctx, mcancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
if _, err := mkapi.Watcher(key, &client.WatcherOptions{AfterIndex: resp.Node.ModifiedIndex - 1}).Next(mctx); err != nil {
|
||||
|
@ -93,7 +93,7 @@ func TestSnapshotAndRestartMember(t *testing.T) {
|
||||
resps := make([]*client.Response, 120)
|
||||
var err error
|
||||
for i := 0; i < 120; i++ {
|
||||
cc := mustNewHTTPClient(t, []string{m.URL()}, nil)
|
||||
cc := MustNewHTTPClient(t, []string{m.URL()}, nil)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
key := fmt.Sprintf("foo%d", i)
|
||||
@ -108,7 +108,7 @@ func TestSnapshotAndRestartMember(t *testing.T) {
|
||||
|
||||
m.WaitOK(t)
|
||||
for i := 0; i < 120; i++ {
|
||||
cc := mustNewHTTPClient(t, []string{m.URL()}, nil)
|
||||
cc := MustNewHTTPClient(t, []string{m.URL()}, nil)
|
||||
kapi := client.NewKeysAPI(cc)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
|
||||
key := fmt.Sprintf("foo%d", i)
|
||||
|
@ -55,7 +55,7 @@ const (
|
||||
type Backend interface {
|
||||
BatchTx() BatchTx
|
||||
Snapshot() Snapshot
|
||||
Hash() (uint32, error)
|
||||
Hash(ignores map[IgnoreKey]struct{}) (uint32, error)
|
||||
// Size returns the current size of the backend.
|
||||
Size() int64
|
||||
Defrag() error
|
||||
@ -144,7 +144,12 @@ func (b *backend) Snapshot() Snapshot {
|
||||
return &snapshot{tx}
|
||||
}
|
||||
|
||||
func (b *backend) Hash() (uint32, error) {
|
||||
type IgnoreKey struct {
|
||||
Bucket string
|
||||
Key string
|
||||
}
|
||||
|
||||
func (b *backend) Hash(ignores map[IgnoreKey]struct{}) (uint32, error) {
|
||||
h := crc32.New(crc32.MakeTable(crc32.Castagnoli))
|
||||
|
||||
b.mu.RLock()
|
||||
@ -158,8 +163,11 @@ func (b *backend) Hash() (uint32, error) {
|
||||
}
|
||||
h.Write(next)
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
h.Write(k)
|
||||
h.Write(v)
|
||||
bk := IgnoreKey{Bucket: string(next), Key: string(k)}
|
||||
if _, ok := ignores[bk]; !ok {
|
||||
h.Write(k)
|
||||
h.Write(v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ func TestBackendDefrag(t *testing.T) {
|
||||
size := b.Size()
|
||||
|
||||
// shrink and check hash
|
||||
oh, err := b.Hash()
|
||||
oh, err := b.Hash(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -151,7 +151,7 @@ func TestBackendDefrag(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nh, err := b.Hash()
|
||||
nh, err := b.Hash(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -320,7 +320,14 @@ func (s *store) Hash() (uint32, int64, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
h, err := s.b.Hash()
|
||||
// ignore hash consistent index field for now.
|
||||
// consistent index might be changed due to v2 internal sync, which
|
||||
// is not controllable by the user.
|
||||
ignores := make(map[backend.IgnoreKey]struct{})
|
||||
bk := backend.IgnoreKey{Bucket: string(metaBucketName), Key: string(consistentIndexKeyName)}
|
||||
ignores[bk] = struct{}{}
|
||||
|
||||
h, err := s.b.Hash(ignores)
|
||||
rev := s.currentRev.main
|
||||
return h, rev, err
|
||||
}
|
||||
|
@ -593,13 +593,13 @@ type fakeBackend struct {
|
||||
tx *fakeBatchTx
|
||||
}
|
||||
|
||||
func (b *fakeBackend) BatchTx() backend.BatchTx { return b.tx }
|
||||
func (b *fakeBackend) Hash() (uint32, error) { return 0, nil }
|
||||
func (b *fakeBackend) Size() int64 { return 0 }
|
||||
func (b *fakeBackend) Snapshot() backend.Snapshot { return nil }
|
||||
func (b *fakeBackend) ForceCommit() {}
|
||||
func (b *fakeBackend) Defrag() error { return nil }
|
||||
func (b *fakeBackend) Close() error { return nil }
|
||||
func (b *fakeBackend) BatchTx() backend.BatchTx { return b.tx }
|
||||
func (b *fakeBackend) Hash(ignores map[backend.IgnoreKey]struct{}) (uint32, error) { return 0, nil }
|
||||
func (b *fakeBackend) Size() int64 { return 0 }
|
||||
func (b *fakeBackend) Snapshot() backend.Snapshot { return nil }
|
||||
func (b *fakeBackend) ForceCommit() {}
|
||||
func (b *fakeBackend) Defrag() error { return nil }
|
||||
func (b *fakeBackend) Close() error { return nil }
|
||||
|
||||
type indexGetResp struct {
|
||||
rev revision
|
||||
|
1
test
1
test
@ -64,6 +64,7 @@ function integration_tests {
|
||||
intpid="$!"
|
||||
wait $e2epid
|
||||
wait $intpid
|
||||
go test -timeout 1m -v ${RACE} -cpu 1,2,4 $@ ${REPO_PATH}/client/integration
|
||||
go test -timeout 10m -v ${RACE} -cpu 1,2,4 $@ ${REPO_PATH}/clientv3/integration
|
||||
go test -timeout 1m -v -cpu 1,2,4 $@ ${REPO_PATH}/contrib/raftexample
|
||||
go test -timeout 1m -v ${RACE} -cpu 1,2,4 -run=Example $@ ${TEST}
|
||||
|
@ -143,7 +143,7 @@ func compactKV(clients []*v3.Client) {
|
||||
revToCompact := max(0, curRev-compactIndexDelta)
|
||||
for _, c := range clients {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
err := c.KV.Compact(ctx, revToCompact)
|
||||
_, err := c.KV.Compact(ctx, revToCompact)
|
||||
cancel()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
var (
|
||||
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
|
||||
MinClusterVersion = "2.3.0"
|
||||
Version = "3.0.0-beta.0+git"
|
||||
Version = "3.0.2"
|
||||
|
||||
// Git SHA Value will be set during build
|
||||
GitSHA = "Not provided (use ./build instead of go build)"
|
||||
|
19
wal/wal.go
19
wal/wal.go
@ -129,15 +129,22 @@ func Create(dirpath string, metadata []byte) (*WAL, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(dirpath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// rename of directory with locked files doesn't work on windows; close
|
||||
// the WAL to release the locks so the directory can be renamed
|
||||
w.Close()
|
||||
if err := os.Rename(tmpdirpath, dirpath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.fp = newFilePipeline(w.dir, segmentSizeBytes)
|
||||
return w, nil
|
||||
// reopen and relock
|
||||
newWAL, oerr := Open(dirpath, walpb.Snapshot{})
|
||||
if oerr != nil {
|
||||
return nil, oerr
|
||||
}
|
||||
if _, _, _, err := newWAL.ReadAll(); err != nil {
|
||||
newWAL.Close()
|
||||
return nil, err
|
||||
}
|
||||
return newWAL, nil
|
||||
}
|
||||
|
||||
// Open opens the WAL at the given snap.
|
||||
|
Reference in New Issue
Block a user