Compare commits

...

29 Commits

Author SHA1 Message Date
faeeb2fc75 version: bump to v3.0.2 2016-07-08 11:45:18 -07:00
d50c487132 v3rpc: lock progress and prevKV map correctly 2016-07-08 10:16:10 -07:00
b837feffe4 client/integration: test v2 client one shot operations 2016-07-07 17:30:09 -07:00
4d89640195 client: make set/delete one shot operations
Old behavior would retry set and delete even if there's an error. This
can lead to the client returning an error for deleting twice, instead
of returning an error for an interdeterminate state.

Fixes #5832
2016-07-07 17:30:04 -07:00
1292d453c3 clientv3: fix sync base
It is not correct to use WithPrefix. Range end will change in every
internal batch.
2016-07-07 14:21:43 -07:00
ec20b381ed clientv3: add public function to get prefix range end 2016-07-07 14:21:41 -07:00
37cc3f5262 Dockerfile: use 'ENTRYPOINT' instead of 'CMD'
use entrypoint, so people can specify flags to etcd
without providing the binary.

Signed-off-by: Secret <haichuang221@163.com>
2016-07-05 11:40:47 -07:00
7f1940e5ed etcdserver: commit before sending snapshot 2016-07-05 11:06:54 -07:00
caccf8e5e6 v3rpc: do not panic on user error for watch 2016-07-05 11:06:35 -07:00
ef65dfe2eb wal: release wal locks before renaming directory on init
Fixes #5852
2016-07-05 11:05:51 -07:00
ff6c6916f2 etcdserver/api: print only major.minor version API
Before

2016-07-01 14:57:50.927170 I | api: enabled capabilities for version 3.0.0

After

2016-07-01 14:57:50.927170 I | api: enabled capabilities for version 3.0
2016-07-01 15:19:53 -07:00
3dfe8765d3 version: bump to v3.0.1+git 2016-07-01 14:53:20 -07:00
a4a52cb15d version: bump to v3.0.1 2016-07-01 13:58:37 -07:00
014970930a *: test, docs with go1.6+
etcd v3 uses http/2, which doesn't work well with go1.5
2016-07-01 11:59:37 -07:00
4628be982c Documentation: fix typo in api_grpc_gateway.md 2016-07-01 11:59:35 -07:00
ff55e5a188 etcdserver: exit on missing backend only if semver is >= 3.0.0 2016-07-01 11:59:32 -07:00
bf0898266c release: fix Dockerfile etcd binary paths
release script uses binary files in 'release/image-docker',
not the ones in "bin/". Tested with v3.0.0 release.
2016-06-30 12:27:34 -07:00
b9d69f7698 version: bump to v3.0.0+git 2016-06-30 11:37:05 -07:00
6f48bda7ac version: bump to v3.0.0 2016-06-30 10:04:59 -07:00
316534e09e *: remove beta from docs 2016-06-30 10:04:34 -07:00
3cecbdb464 hack: install goreman in tls-setup example 2016-06-30 09:33:19 -07:00
62f11e43ee hack: add tls-setup example generated certs to gitignore 2016-06-30 09:33:12 -07:00
064c1585ee Merge pull request #5822 from raoofm/patch-9
Doc: fix typo in dev-guide.md
2016-06-30 09:06:32 -07:00
15300a1eb8 Doc: fix typo in dev-guide.md 2016-06-30 10:36:50 -04:00
58dd047ee4 ctlv3: make flags, commands formats consistent
1. Capitalize first letter
2. Remove period at the end

(followed the pattern in linux coreutil man page)
2016-06-29 16:16:56 -07:00
4b42ea6cd7 clientv3: only use closeErr on watch when donec is closed
Fixes #5800
2016-06-28 17:48:44 -07:00
53c27ae621 benchmark: fix Compact request 2016-06-28 14:15:32 -07:00
269de67bde mvcc: do not hash consistent index 2016-06-28 12:29:36 -07:00
8bbccf1047 clientv3, ctl3, clientv3/integration: add compact response to compact 2016-06-28 12:29:32 -07:00
57 changed files with 447 additions and 185 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@
/hack/insta-discovery/.env
*.test
tools/functional-tester/docker/bin
hack/tls-setup/certs

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -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 {

View 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
},
}
}

View 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)
}

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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))

View File

@ -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 {

View File

@ -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
}

View File

@ -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)

View File

@ -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:

View File

@ -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\>

View File

@ -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

View File

@ -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,
}
}

View File

@ -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)

View File

@ -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,
}
}

View File

@ -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,
}

View File

@ -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")

View File

@ -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.
`,

View File

@ -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
}

View File

@ -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,
}

View File

@ -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

View File

@ -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
}

View File

@ -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.
`,

View File

@ -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
}

View File

@ -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:

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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()))
}
}

View File

@ -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
}

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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
})
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
View File

@ -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}

View File

@ -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)

View File

@ -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)"

View File

@ -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.