switch from godep to glide
This commit is contained in:
+2
-2
@@ -21,9 +21,9 @@ import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
|
||||
math "math"
|
||||
)
|
||||
|
||||
import io "io"
|
||||
io "io"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package authpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.sizer_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
option (gogoproto.goproto_getters_all) = false;
|
||||
option (gogoproto.goproto_enum_prefix_all) = false;
|
||||
|
||||
// User is a single entry in the bucket authUsers
|
||||
message User {
|
||||
bytes name = 1;
|
||||
bytes password = 2;
|
||||
repeated string roles = 3;
|
||||
}
|
||||
|
||||
// Permission is a single entity
|
||||
message Permission {
|
||||
enum Type {
|
||||
READ = 0;
|
||||
WRITE = 1;
|
||||
READWRITE = 2;
|
||||
}
|
||||
Type permType = 1;
|
||||
|
||||
bytes key = 2;
|
||||
bytes range_end = 3;
|
||||
}
|
||||
|
||||
// Role is a single entry in the bucket authRoles
|
||||
message Role {
|
||||
bytes name = 1;
|
||||
|
||||
repeated Permission keyPermission = 2;
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
# etcd/client
|
||||
|
||||
etcd/client is the Go client library for etcd.
|
||||
|
||||
[](https://godoc.org/github.com/coreos/etcd/client)
|
||||
|
||||
etcd uses `cmd/vendor` directory to store external dependencies, which are
|
||||
to be compiled into etcd release binaries. `client` can be imported without
|
||||
vendoring. For full compatibility, it is recommended to vendor builds using
|
||||
etcd's vendored packages, using tools like godep, as in
|
||||
[vendor directories](https://golang.org/cmd/go/#hdr-Vendor_Directories).
|
||||
For more detail, please read [Go vendor design](https://golang.org/s/go15vendor).
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/coreos/etcd/client
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"github.com/coreos/etcd/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := client.Config{
|
||||
Endpoints: []string{"http://127.0.0.1:2379"},
|
||||
Transport: client.DefaultTransport,
|
||||
// set timeout per request to fail fast when the target endpoint is unavailable
|
||||
HeaderTimeoutPerRequest: time.Second,
|
||||
}
|
||||
c, err := client.New(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
kapi := client.NewKeysAPI(c)
|
||||
// set "/foo" key with "bar" value
|
||||
log.Print("Setting '/foo' key with 'bar' value")
|
||||
resp, err := kapi.Set(context.Background(), "/foo", "bar", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
// print common key info
|
||||
log.Printf("Set is done. Metadata is %q\n", resp)
|
||||
}
|
||||
// get "/foo" key's value
|
||||
log.Print("Getting '/foo' key value")
|
||||
resp, err = kapi.Get(context.Background(), "/foo", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
} else {
|
||||
// print common key info
|
||||
log.Printf("Get is done. Metadata is %q\n", resp)
|
||||
// print value
|
||||
log.Printf("%q key has %q value\n", resp.Node.Key, resp.Node.Value)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
etcd client might return three types of errors.
|
||||
|
||||
- context error
|
||||
|
||||
Each API call has its first parameter as `context`. A context can be canceled or have an attached deadline. If the context is canceled or reaches its deadline, the responding context error will be returned no matter what internal errors the API call has already encountered.
|
||||
|
||||
- cluster error
|
||||
|
||||
Each API call tries to send request to the cluster endpoints one by one until it successfully gets a response. If a requests to an endpoint fails, due to exceeding per request timeout or connection issues, the error will be added into a list of errors. If all possible endpoints fail, a cluster error that includes all encountered errors will be returned.
|
||||
|
||||
- response error
|
||||
|
||||
If the response gets from the cluster is invalid, a plain string error will be returned. For example, it might be a invalid JSON error.
|
||||
|
||||
Here is the example code to handle client errors:
|
||||
|
||||
```go
|
||||
cfg := client.Config{Endpoints: []string{"http://etcd1:2379","http://etcd2:2379","http://etcd3:2379"}}
|
||||
c, err := client.New(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
kapi := client.NewKeysAPI(c)
|
||||
resp, err := kapi.Set(ctx, "test", "bar", nil)
|
||||
if err != nil {
|
||||
if err == context.Canceled {
|
||||
// ctx is canceled by another routine
|
||||
} else if err == context.DeadlineExceeded {
|
||||
// ctx is attached with a deadline and it exceeded
|
||||
} else if cerr, ok := err.(*client.ClusterError); ok {
|
||||
// process (cerr.Errors)
|
||||
} else {
|
||||
// bad cluster endpoints, which are not etcd servers
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Caveat
|
||||
|
||||
1. etcd/client prefers to use the same endpoint as long as the endpoint continues to work well. This saves socket resources, and improves efficiency for both client and server side. This preference doesn't remove consistency from the data consumed by the client because data replicated to each etcd member has already passed through the consensus process.
|
||||
|
||||
2. etcd/client does round-robin rotation on other available endpoints if the preferred endpoint isn't functioning properly. For example, if the member that etcd/client connects to is hard killed, etcd/client will fail on the first attempt with the killed member, and succeed on the second attempt with another member. If it fails to talk to all available endpoints, it will return all errors happened.
|
||||
|
||||
3. Default etcd/client cannot handle the case that the remote server is SIGSTOPed now. TCP keepalive mechanism doesn't help in this scenario because operating system may still send TCP keep-alive packets. Over time we'd like to improve this functionality, but solving this issue isn't high priority because a real-life case in which a server is stopped, but the connection is kept alive, hasn't been brought to our attention.
|
||||
|
||||
4. etcd/client cannot detect whether the member in use is healthy when doing read requests. If the member is isolated from the cluster, etcd/client may retrieve outdated data. As a workaround, users could monitor experimental /health endpoint for member healthy information. We are improving it at [#3265](https://github.com/coreos/etcd/issues/3265).
|
||||
+2
-2
@@ -305,7 +305,7 @@ func (c *httpClusterClient) SetEndpoints(eps []string) error {
|
||||
// If endpoints doesn't have the lu, just keep c.pinned = 0.
|
||||
// Forwarding between follower and leader would be required but it works.
|
||||
default:
|
||||
return errors.New(fmt.Sprintf("invalid endpoint selection mode: %d", c.selectionMode))
|
||||
return fmt.Errorf("invalid endpoint selection mode: %d", c.selectionMode)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -404,7 +404,7 @@ func (c *httpClusterClient) Sync(ctx context.Context) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
eps := make([]string, 0)
|
||||
var eps []string
|
||||
for _, m := range ms {
|
||||
eps = append(eps, m.ClientURLs...)
|
||||
}
|
||||
|
||||
+5
-1
@@ -21,7 +21,11 @@ type ClusterError struct {
|
||||
}
|
||||
|
||||
func (ce *ClusterError) Error() string {
|
||||
return ErrClusterUnavailable.Error()
|
||||
s := ErrClusterUnavailable.Error()
|
||||
for i, e := range ce.Errors {
|
||||
s += fmt.Sprintf("; error #%d: %s\n", i, e)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (ce *ClusterError) Detail() string {
|
||||
|
||||
+2
-1
@@ -8,10 +8,11 @@ package client
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
codec1978 "github.com/ugorji/go/codec"
|
||||
"reflect"
|
||||
"runtime"
|
||||
time "time"
|
||||
|
||||
codec1978 "github.com/ugorji/go/codec"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+18
-9
@@ -191,6 +191,10 @@ type SetOptions struct {
|
||||
|
||||
// Dir specifies whether or not this Node should be created as a directory.
|
||||
Dir bool
|
||||
|
||||
// NoValueOnSuccess specifies whether the response contains the current value of the Node.
|
||||
// If set, the response will only contain the current value when the request fails.
|
||||
NoValueOnSuccess bool
|
||||
}
|
||||
|
||||
type GetOptions struct {
|
||||
@@ -335,6 +339,7 @@ func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions
|
||||
act.TTL = opts.TTL
|
||||
act.Refresh = opts.Refresh
|
||||
act.Dir = opts.Dir
|
||||
act.NoValueOnSuccess = opts.NoValueOnSuccess
|
||||
}
|
||||
|
||||
doCtx := ctx
|
||||
@@ -523,15 +528,16 @@ func (w *waitAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
}
|
||||
|
||||
type setAction struct {
|
||||
Prefix string
|
||||
Key string
|
||||
Value string
|
||||
PrevValue string
|
||||
PrevIndex uint64
|
||||
PrevExist PrevExistType
|
||||
TTL time.Duration
|
||||
Refresh bool
|
||||
Dir bool
|
||||
Prefix string
|
||||
Key string
|
||||
Value string
|
||||
PrevValue string
|
||||
PrevIndex uint64
|
||||
PrevExist PrevExistType
|
||||
TTL time.Duration
|
||||
Refresh bool
|
||||
Dir bool
|
||||
NoValueOnSuccess bool
|
||||
}
|
||||
|
||||
func (a *setAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
@@ -565,6 +571,9 @@ func (a *setAction) HTTPRequest(ep url.URL) *http.Request {
|
||||
if a.Refresh {
|
||||
form.Add("refresh", "true")
|
||||
}
|
||||
if a.NoValueOnSuccess {
|
||||
params.Set("noValueOnSuccess", strconv.FormatBool(a.NoValueOnSuccess))
|
||||
}
|
||||
|
||||
u.RawQuery = params.Encode()
|
||||
body := strings.NewReader(form.Encode())
|
||||
|
||||
+30
@@ -14,6 +14,20 @@
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
roleNotFoundRegExp *regexp.Regexp
|
||||
userNotFoundRegExp *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
roleNotFoundRegExp = regexp.MustCompile("auth: Role .* does not exist.")
|
||||
userNotFoundRegExp = regexp.MustCompile("auth: User .* does not exist.")
|
||||
}
|
||||
|
||||
// IsKeyNotFound returns true if the error code is ErrorCodeKeyNotFound.
|
||||
func IsKeyNotFound(err error) bool {
|
||||
if cErr, ok := err.(Error); ok {
|
||||
@@ -21,3 +35,19 @@ func IsKeyNotFound(err error) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRoleNotFound returns true if the error means role not found of v2 API.
|
||||
func IsRoleNotFound(err error) bool {
|
||||
if ae, ok := err.(authError); ok {
|
||||
return roleNotFoundRegExp.MatchString(ae.Message)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUserNotFound returns true if the error means user not found of v2 API.
|
||||
func IsUserNotFound(err error) bool {
|
||||
if ae, ok := err.(authError); ok {
|
||||
return userNotFoundRegExp.MatchString(ae.Message)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
# etcd/clientv3
|
||||
|
||||
[](https://godoc.org/github.com/coreos/etcd/clientv3)
|
||||
|
||||
`etcd/clientv3` is the official Go etcd client for v3.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/coreos/etcd/clientv3
|
||||
```
|
||||
|
||||
## Get started
|
||||
|
||||
Create client using `clientv3.New`:
|
||||
|
||||
```go
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: []string{"localhost:2379", "localhost:22379", "localhost:32379"},
|
||||
DialTimeout: 5 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
// handle error!
|
||||
}
|
||||
defer cli.Close()
|
||||
```
|
||||
|
||||
etcd v3 uses [`gRPC`](http://www.grpc.io) for remote procedure calls. And `clientv3` uses
|
||||
[`grpc-go`](https://github.com/grpc/grpc-go) to connect to etcd. Make sure to close the client after using it.
|
||||
If the client is not closed, the connection will have leaky goroutines. To specify client request timeout,
|
||||
pass `context.WithTimeout` to APIs:
|
||||
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
resp, err := kvc.Put(ctx, "sample_key", "sample_value")
|
||||
cancel()
|
||||
if err != nil {
|
||||
// handle error!
|
||||
}
|
||||
// use the response
|
||||
```
|
||||
|
||||
etcd uses `cmd/vendor` directory to store external dependencies, which are
|
||||
to be compiled into etcd release binaries. `client` can be imported without
|
||||
vendoring. For full compatibility, it is recommended to vendor builds using
|
||||
etcd's vendored packages, using tools like godep, as in
|
||||
[vendor directories](https://golang.org/cmd/go/#hdr-Vendor_Directories).
|
||||
For more detail, please read [Go vendor design](https://golang.org/s/go15vendor).
|
||||
|
||||
## Error Handling
|
||||
|
||||
etcd client returns 2 types of errors:
|
||||
|
||||
1. context error: canceled or deadline exceeded.
|
||||
2. gRPC error: see [api/v3rpc/rpctypes](https://godoc.org/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes).
|
||||
|
||||
Here is the example code to handle client errors:
|
||||
|
||||
```go
|
||||
resp, err := kvc.Put(ctx, "", "")
|
||||
if err != nil {
|
||||
switch err {
|
||||
case context.Canceled:
|
||||
log.Fatalf("ctx is canceled by another routine: %v", err)
|
||||
case context.DeadlineExceeded:
|
||||
log.Fatalf("ctx is attached with a deadline is exceeded: %v", err)
|
||||
case rpctypes.ErrEmptyKey:
|
||||
log.Fatalf("client-side error: %v", err)
|
||||
default:
|
||||
log.Fatalf("bad cluster endpoints, which are not etcd servers: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
More code examples can be found at [GoDoc](https://godoc.org/github.com/coreos/etcd/clientv3).
|
||||
+1
@@ -43,6 +43,7 @@ type (
|
||||
AuthRoleListResponse pb.AuthRoleListResponse
|
||||
|
||||
PermissionType authpb.Permission_Type
|
||||
Permission authpb.Permission
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+70
-3
@@ -42,9 +42,16 @@ type simpleBalancer struct {
|
||||
// upc closes when upEps transitions from empty to non-zero or the balancer closes.
|
||||
upc chan struct{}
|
||||
|
||||
// grpc issues TLS cert checks using the string passed into dial so
|
||||
// that string must be the host. To recover the full scheme://host URL,
|
||||
// have a map from hosts to the original endpoint.
|
||||
host2ep map[string]string
|
||||
|
||||
// pinAddr is the currently pinned address; set to the empty string on
|
||||
// intialization and shutdown.
|
||||
pinAddr string
|
||||
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newSimpleBalancer(eps []string) *simpleBalancer {
|
||||
@@ -60,11 +67,12 @@ func newSimpleBalancer(eps []string) *simpleBalancer {
|
||||
readyc: make(chan struct{}),
|
||||
upEps: make(map[string]struct{}),
|
||||
upc: make(chan struct{}),
|
||||
host2ep: getHost2ep(eps),
|
||||
}
|
||||
return sb
|
||||
}
|
||||
|
||||
func (b *simpleBalancer) Start(target string) error { return nil }
|
||||
func (b *simpleBalancer) Start(target string, config grpc.BalancerConfig) error { return nil }
|
||||
|
||||
func (b *simpleBalancer) ConnectNotify() <-chan struct{} {
|
||||
b.mu.Lock()
|
||||
@@ -72,17 +80,70 @@ func (b *simpleBalancer) ConnectNotify() <-chan struct{} {
|
||||
return b.upc
|
||||
}
|
||||
|
||||
func (b *simpleBalancer) getEndpoint(host string) string {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.host2ep[host]
|
||||
}
|
||||
|
||||
func getHost2ep(eps []string) map[string]string {
|
||||
hm := make(map[string]string, len(eps))
|
||||
for i := range eps {
|
||||
_, host, _ := parseEndpoint(eps[i])
|
||||
hm[host] = eps[i]
|
||||
}
|
||||
return hm
|
||||
}
|
||||
|
||||
func (b *simpleBalancer) updateAddrs(eps []string) {
|
||||
np := getHost2ep(eps)
|
||||
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
match := len(np) == len(b.host2ep)
|
||||
for k, v := range np {
|
||||
if b.host2ep[k] != v {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
// same endpoints, so no need to update address
|
||||
return
|
||||
}
|
||||
|
||||
b.host2ep = np
|
||||
|
||||
addrs := make([]grpc.Address, 0, len(eps))
|
||||
for i := range eps {
|
||||
addrs = append(addrs, grpc.Address{Addr: getHost(eps[i])})
|
||||
}
|
||||
b.addrs = addrs
|
||||
b.notifyCh <- addrs
|
||||
}
|
||||
|
||||
func (b *simpleBalancer) Up(addr grpc.Address) func(error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
// gRPC might call Up after it called Close. We add this check
|
||||
// to "fix" it up at application layer. Or our simplerBalancer
|
||||
// might panic since b.upc is closed.
|
||||
if b.closed {
|
||||
return func(err error) {}
|
||||
}
|
||||
|
||||
if len(b.upEps) == 0 {
|
||||
// notify waiting Get()s and pin first connected address
|
||||
close(b.upc)
|
||||
b.pinAddr = addr.Addr
|
||||
}
|
||||
b.upEps[addr.Addr] = struct{}{}
|
||||
b.mu.Unlock()
|
||||
|
||||
// notify client that a connection is up
|
||||
b.readyOnce.Do(func() { close(b.readyc) })
|
||||
|
||||
return func(err error) {
|
||||
b.mu.Lock()
|
||||
delete(b.upEps, addr.Addr)
|
||||
@@ -128,13 +189,19 @@ func (b *simpleBalancer) Notify() <-chan []grpc.Address { return b.notifyCh }
|
||||
|
||||
func (b *simpleBalancer) Close() error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
// In case gRPC calls close twice. TODO: remove the checking
|
||||
// when we are sure that gRPC wont call close twice.
|
||||
if b.closed {
|
||||
return nil
|
||||
}
|
||||
b.closed = true
|
||||
close(b.notifyCh)
|
||||
// terminate all waiting Get()s
|
||||
b.pinAddr = ""
|
||||
if len(b.upEps) == 0 {
|
||||
close(b.upc)
|
||||
}
|
||||
b.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+81
-38
@@ -18,8 +18,6 @@ import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -29,6 +27,7 @@ import (
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
@@ -98,6 +97,44 @@ func (c *Client) Ctx() context.Context { return c.ctx }
|
||||
// Endpoints lists the registered endpoints for the client.
|
||||
func (c *Client) Endpoints() []string { return c.cfg.Endpoints }
|
||||
|
||||
// SetEndpoints updates client's endpoints.
|
||||
func (c *Client) SetEndpoints(eps ...string) {
|
||||
c.cfg.Endpoints = eps
|
||||
c.balancer.updateAddrs(eps)
|
||||
}
|
||||
|
||||
// Sync synchronizes client's endpoints with the known endpoints from the etcd membership.
|
||||
func (c *Client) Sync(ctx context.Context) error {
|
||||
mresp, err := c.MemberList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var eps []string
|
||||
for _, m := range mresp.Members {
|
||||
eps = append(eps, m.ClientURLs...)
|
||||
}
|
||||
c.SetEndpoints(eps...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) autoSync() {
|
||||
if c.cfg.AutoSyncInterval == time.Duration(0) {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.ctx.Done():
|
||||
return
|
||||
case <-time.After(c.cfg.AutoSyncInterval):
|
||||
ctx, _ := context.WithTimeout(c.ctx, 5*time.Second)
|
||||
if err := c.Sync(ctx); err != nil && err != c.ctx.Err() {
|
||||
logger.Println("Auto sync endpoints failed:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type authTokenCredential struct {
|
||||
token string
|
||||
}
|
||||
@@ -112,19 +149,31 @@ func (cred authTokenCredential) GetRequestMetadata(ctx context.Context, s ...str
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialTarget(endpoint string) (proto string, host string, creds *credentials.TransportCredentials) {
|
||||
func parseEndpoint(endpoint string) (proto string, host string, scheme string) {
|
||||
proto = "tcp"
|
||||
host = endpoint
|
||||
creds = c.creds
|
||||
url, uerr := url.Parse(endpoint)
|
||||
if uerr != nil || !strings.Contains(endpoint, "://") {
|
||||
return
|
||||
}
|
||||
scheme = url.Scheme
|
||||
|
||||
// strip scheme:// prefix since grpc dials by host
|
||||
host = url.Host
|
||||
switch url.Scheme {
|
||||
case "http", "https":
|
||||
case "unix":
|
||||
proto = "unix"
|
||||
default:
|
||||
proto, host = "", ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) processCreds(scheme string) (creds *credentials.TransportCredentials) {
|
||||
creds = c.creds
|
||||
switch scheme {
|
||||
case "unix":
|
||||
case "http":
|
||||
creds = nil
|
||||
case "https":
|
||||
@@ -135,7 +184,7 @@ func (c *Client) dialTarget(endpoint string) (proto string, host string, creds *
|
||||
emptyCreds := credentials.NewTLS(tlsconfig)
|
||||
creds = &emptyCreds
|
||||
default:
|
||||
return "", "", nil
|
||||
creds = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -147,17 +196,8 @@ func (c *Client) dialSetupOpts(endpoint string, dopts ...grpc.DialOption) (opts
|
||||
}
|
||||
opts = append(opts, dopts...)
|
||||
|
||||
// grpc issues TLS cert checks using the string passed into dial so
|
||||
// that string must be the host. To recover the full scheme://host URL,
|
||||
// have a map from hosts to the original endpoint.
|
||||
host2ep := make(map[string]string)
|
||||
for i := range c.cfg.Endpoints {
|
||||
_, host, _ := c.dialTarget(c.cfg.Endpoints[i])
|
||||
host2ep[host] = c.cfg.Endpoints[i]
|
||||
}
|
||||
|
||||
f := func(host string, t time.Duration) (net.Conn, error) {
|
||||
proto, host, _ := c.dialTarget(host2ep[host])
|
||||
proto, host, _ := parseEndpoint(c.balancer.getEndpoint(host))
|
||||
if proto == "" {
|
||||
return nil, fmt.Errorf("unknown scheme for %q", host)
|
||||
}
|
||||
@@ -170,7 +210,10 @@ func (c *Client) dialSetupOpts(endpoint string, dopts ...grpc.DialOption) (opts
|
||||
}
|
||||
opts = append(opts, grpc.WithDialer(f))
|
||||
|
||||
_, _, creds := c.dialTarget(endpoint)
|
||||
creds := c.creds
|
||||
if _, _, scheme := parseEndpoint(endpoint); len(scheme) != 0 {
|
||||
creds = c.processCreds(scheme)
|
||||
}
|
||||
if creds != nil {
|
||||
opts = append(opts, grpc.WithTransportCredentials(*creds))
|
||||
} else {
|
||||
@@ -272,13 +315,8 @@ func newClient(cfg *Config) (*Client, error) {
|
||||
client.Watcher = NewWatcher(client)
|
||||
client.Auth = NewAuth(client)
|
||||
client.Maintenance = NewMaintenance(client)
|
||||
if cfg.Logger != nil {
|
||||
logger.Set(cfg.Logger)
|
||||
} else {
|
||||
// disable client side grpc by default
|
||||
logger.Set(log.New(ioutil.Discard, "", 0))
|
||||
}
|
||||
|
||||
go client.autoSync()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -294,17 +332,14 @@ func isHaltErr(ctx context.Context, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
eErr := rpctypes.Error(err)
|
||||
if _, ok := eErr.(rpctypes.EtcdError); ok {
|
||||
return eErr != rpctypes.ErrStopped && eErr != rpctypes.ErrNoLeader
|
||||
}
|
||||
// treat etcdserver errors not recognized by the client as halting
|
||||
return isConnClosing(err) || strings.Contains(err.Error(), "etcdserver:")
|
||||
}
|
||||
|
||||
// isConnClosing returns true if the error matches a grpc client closing error
|
||||
func isConnClosing(err error) bool {
|
||||
return strings.Contains(err.Error(), grpc.ErrClientConnClosing.Error())
|
||||
code := grpc.Code(err)
|
||||
// Unavailable codes mean the system will be right back.
|
||||
// (e.g., can't connect, lost leader)
|
||||
// Treat Internal codes as if something failed, leaving the
|
||||
// system in an inconsistent state, but retrying could make progress.
|
||||
// (e.g., failed in middle of send, corrupted frame)
|
||||
// TODO: are permanent Internal errors possible from grpc?
|
||||
return code != codes.Unavailable && code != codes.Internal
|
||||
}
|
||||
|
||||
func toErr(ctx context.Context, err error) error {
|
||||
@@ -312,12 +347,20 @@ func toErr(ctx context.Context, err error) error {
|
||||
return nil
|
||||
}
|
||||
err = rpctypes.Error(err)
|
||||
switch {
|
||||
case ctx.Err() != nil && strings.Contains(err.Error(), "context"):
|
||||
err = ctx.Err()
|
||||
case strings.Contains(err.Error(), ErrNoAvailableEndpoints.Error()):
|
||||
if _, ok := err.(rpctypes.EtcdError); ok {
|
||||
return err
|
||||
}
|
||||
code := grpc.Code(err)
|
||||
switch code {
|
||||
case codes.DeadlineExceeded:
|
||||
fallthrough
|
||||
case codes.Canceled:
|
||||
if ctx.Err() != nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
case codes.Unavailable:
|
||||
err = ErrNoAvailableEndpoints
|
||||
case strings.Contains(err.Error(), grpc.ErrClientConnClosing.Error()):
|
||||
case codes.FailedPrecondition:
|
||||
err = grpc.ErrClientConnClosing
|
||||
}
|
||||
return err
|
||||
|
||||
+8
-5
@@ -28,15 +28,16 @@ type Config struct {
|
||||
// Endpoints is a list of URLs
|
||||
Endpoints []string
|
||||
|
||||
// AutoSyncInterval is the interval to update endpoints with its latest members.
|
||||
// 0 disables auto-sync. By default auto-sync is disabled.
|
||||
AutoSyncInterval time.Duration
|
||||
|
||||
// DialTimeout is the timeout for failing to establish a connection.
|
||||
DialTimeout time.Duration
|
||||
|
||||
// TLS holds the client secure credentials, if any.
|
||||
TLS *tls.Config
|
||||
|
||||
// Logger is the logger used by client library.
|
||||
Logger Logger
|
||||
|
||||
// Username is a username for authentication
|
||||
Username string
|
||||
|
||||
@@ -46,6 +47,7 @@ type Config struct {
|
||||
|
||||
type yamlConfig struct {
|
||||
Endpoints []string `json:"endpoints"`
|
||||
AutoSyncInterval time.Duration `json:"auto-sync-interval"`
|
||||
DialTimeout time.Duration `json:"dial-timeout"`
|
||||
InsecureTransport bool `json:"insecure-transport"`
|
||||
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify"`
|
||||
@@ -68,8 +70,9 @@ func configFromFile(fpath string) (*Config, error) {
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Endpoints: yc.Endpoints,
|
||||
DialTimeout: yc.DialTimeout,
|
||||
Endpoints: yc.Endpoints,
|
||||
AutoSyncInterval: yc.AutoSyncInterval,
|
||||
DialTimeout: yc.DialTimeout,
|
||||
}
|
||||
|
||||
if yc.InsecureTransport {
|
||||
|
||||
+7
-17
@@ -85,6 +85,10 @@ func NewKV(c *Client) KV {
|
||||
return &kv{remote: RetryKVClient(c)}
|
||||
}
|
||||
|
||||
func NewKVFromKVClient(remote pb.KVClient) KV {
|
||||
return &kv{remote: remote}
|
||||
}
|
||||
|
||||
func (kv *kv) Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) {
|
||||
r, err := kv.Do(ctx, OpPut(key, val, opts...))
|
||||
return r.put, toErr(ctx, err)
|
||||
@@ -137,34 +141,20 @@ func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
|
||||
// TODO: handle other ops
|
||||
case tRange:
|
||||
var resp *pb.RangeResponse
|
||||
r := &pb.RangeRequest{
|
||||
Key: op.key,
|
||||
RangeEnd: op.end,
|
||||
Limit: op.limit,
|
||||
Revision: op.rev,
|
||||
Serializable: op.serializable,
|
||||
KeysOnly: op.keysOnly,
|
||||
CountOnly: op.countOnly,
|
||||
}
|
||||
if op.sort != nil {
|
||||
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
|
||||
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
|
||||
}
|
||||
|
||||
resp, err = kv.remote.Range(ctx, r, grpc.FailFast(false))
|
||||
resp, err = kv.remote.Range(ctx, op.toRangeRequest(), grpc.FailFast(false))
|
||||
if err == nil {
|
||||
return OpResponse{get: (*GetResponse)(resp)}, nil
|
||||
}
|
||||
case tPut:
|
||||
var resp *pb.PutResponse
|
||||
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID)}
|
||||
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV}
|
||||
resp, err = kv.remote.Put(ctx, r)
|
||||
if err == nil {
|
||||
return OpResponse{put: (*PutResponse)(resp)}, nil
|
||||
}
|
||||
case tDeleteRange:
|
||||
var resp *pb.DeleteRangeResponse
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end}
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
|
||||
resp, err = kv.remote.DeleteRange(ctx, r)
|
||||
if err == nil {
|
||||
return OpResponse{del: (*DeleteResponse)(resp)}, nil
|
||||
|
||||
+44
-2
@@ -44,6 +44,21 @@ type LeaseKeepAliveResponse struct {
|
||||
TTL int64
|
||||
}
|
||||
|
||||
// LeaseTimeToLiveResponse is used to convert the protobuf lease timetolive response.
|
||||
type LeaseTimeToLiveResponse struct {
|
||||
*pb.ResponseHeader
|
||||
ID LeaseID `json:"id"`
|
||||
|
||||
// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds.
|
||||
TTL int64 `json:"ttl"`
|
||||
|
||||
// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
|
||||
GrantedTTL int64 `json:"granted-ttl"`
|
||||
|
||||
// Keys is the list of keys attached to this lease.
|
||||
Keys [][]byte `json:"keys"`
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultTTL is the assumed lease TTL used for the first keepalive
|
||||
// deadline before the actual TTL is known to the client.
|
||||
@@ -61,6 +76,9 @@ type Lease interface {
|
||||
// Revoke revokes the given lease.
|
||||
Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error)
|
||||
|
||||
// TimeToLive retrieves the lease information of the given lease ID.
|
||||
TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error)
|
||||
|
||||
// KeepAlive keeps the given lease alive forever.
|
||||
KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)
|
||||
|
||||
@@ -141,7 +159,7 @@ func (l *lessor) Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, err
|
||||
return gresp, nil
|
||||
}
|
||||
if isHaltErr(cctx, err) {
|
||||
return nil, toErr(ctx, err)
|
||||
return nil, toErr(cctx, err)
|
||||
}
|
||||
if nerr := l.newStream(); nerr != nil {
|
||||
return nil, nerr
|
||||
@@ -170,6 +188,30 @@ func (l *lessor) Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lessor) TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error) {
|
||||
cctx, cancel := context.WithCancel(ctx)
|
||||
done := cancelWhenStop(cancel, l.stopCtx.Done())
|
||||
defer close(done)
|
||||
|
||||
for {
|
||||
r := toLeaseTimeToLiveRequest(id, opts...)
|
||||
resp, err := l.remote.LeaseTimeToLive(cctx, r)
|
||||
if err == nil {
|
||||
gresp := &LeaseTimeToLiveResponse{
|
||||
ResponseHeader: resp.GetHeader(),
|
||||
ID: LeaseID(resp.ID),
|
||||
TTL: resp.TTL,
|
||||
GrantedTTL: resp.GrantedTTL,
|
||||
Keys: resp.Keys,
|
||||
}
|
||||
return gresp, nil
|
||||
}
|
||||
if isHaltErr(cctx, err) {
|
||||
return nil, toErr(cctx, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error) {
|
||||
ch := make(chan *LeaseKeepAliveResponse, leaseResponseChSize)
|
||||
|
||||
@@ -390,7 +432,7 @@ func (l *lessor) sendKeepAliveLoop(stream pb.Lease_LeaseKeepAliveClient) {
|
||||
return
|
||||
}
|
||||
|
||||
tosend := make([]LeaseID, 0)
|
||||
var tosend []LeaseID
|
||||
|
||||
now := time.Now()
|
||||
l.mu.Lock()
|
||||
|
||||
+29
-11
@@ -15,13 +15,15 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
// Logger is the logger used by client library.
|
||||
// It implements grpclog.Logger interface.
|
||||
type Logger grpclog.Logger
|
||||
|
||||
var (
|
||||
@@ -34,20 +36,36 @@ type settableLogger struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
// use go's standard logger by default like grpc
|
||||
// disable client side logs by default
|
||||
logger.mu.Lock()
|
||||
logger.l = log.New(os.Stderr, "", log.LstdFlags)
|
||||
logger.l = log.New(ioutil.Discard, "", 0)
|
||||
|
||||
// logger has to override the grpclog at initialization so that
|
||||
// any changes to the grpclog go through logger with locking
|
||||
// instead of through SetLogger
|
||||
//
|
||||
// now updates only happen through settableLogger.set
|
||||
grpclog.SetLogger(&logger)
|
||||
logger.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *settableLogger) Set(l Logger) {
|
||||
// SetLogger sets client-side Logger. By default, logs are disabled.
|
||||
func SetLogger(l Logger) {
|
||||
logger.set(l)
|
||||
}
|
||||
|
||||
// GetLogger returns the current logger.
|
||||
func GetLogger() Logger {
|
||||
return logger.get()
|
||||
}
|
||||
|
||||
func (s *settableLogger) set(l Logger) {
|
||||
s.mu.Lock()
|
||||
logger.l = l
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *settableLogger) Get() Logger {
|
||||
func (s *settableLogger) get() Logger {
|
||||
s.mu.RLock()
|
||||
l := logger.l
|
||||
s.mu.RUnlock()
|
||||
@@ -56,9 +74,9 @@ func (s *settableLogger) Get() Logger {
|
||||
|
||||
// implement the grpclog.Logger interface
|
||||
|
||||
func (s *settableLogger) Fatal(args ...interface{}) { s.Get().Fatal(args...) }
|
||||
func (s *settableLogger) Fatalf(format string, args ...interface{}) { s.Get().Fatalf(format, args...) }
|
||||
func (s *settableLogger) Fatalln(args ...interface{}) { s.Get().Fatalln(args...) }
|
||||
func (s *settableLogger) Print(args ...interface{}) { s.Get().Print(args...) }
|
||||
func (s *settableLogger) Printf(format string, args ...interface{}) { s.Get().Printf(format, args...) }
|
||||
func (s *settableLogger) Println(args ...interface{}) { s.Get().Println(args...) }
|
||||
func (s *settableLogger) Fatal(args ...interface{}) { s.get().Fatal(args...) }
|
||||
func (s *settableLogger) Fatalf(format string, args ...interface{}) { s.get().Fatalf(format, args...) }
|
||||
func (s *settableLogger) Fatalln(args ...interface{}) { s.get().Fatalln(args...) }
|
||||
func (s *settableLogger) Print(args ...interface{}) { s.get().Print(args...) }
|
||||
func (s *settableLogger) Printf(format string, args ...interface{}) { s.get().Printf(format, args...) }
|
||||
func (s *settableLogger) Println(args ...interface{}) { s.get().Println(args...) }
|
||||
|
||||
+138
-22
@@ -14,9 +14,7 @@
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
import pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
|
||||
type opType int
|
||||
|
||||
@@ -43,40 +41,63 @@ type Op struct {
|
||||
serializable bool
|
||||
keysOnly bool
|
||||
countOnly bool
|
||||
minModRev int64
|
||||
maxModRev int64
|
||||
minCreateRev int64
|
||||
maxCreateRev int64
|
||||
|
||||
// for range, watch
|
||||
rev int64
|
||||
|
||||
// for watch, put, delete
|
||||
prevKV bool
|
||||
|
||||
// progressNotify is for progress updates.
|
||||
progressNotify bool
|
||||
// createdNotify is for created event
|
||||
createdNotify bool
|
||||
// filters for watchers
|
||||
filterPut bool
|
||||
filterDelete bool
|
||||
|
||||
// for put
|
||||
val []byte
|
||||
leaseID LeaseID
|
||||
}
|
||||
|
||||
func (op Op) toRangeRequest() *pb.RangeRequest {
|
||||
if op.t != tRange {
|
||||
panic("op.t != tRange")
|
||||
}
|
||||
r := &pb.RangeRequest{
|
||||
Key: op.key,
|
||||
RangeEnd: op.end,
|
||||
Limit: op.limit,
|
||||
Revision: op.rev,
|
||||
Serializable: op.serializable,
|
||||
KeysOnly: op.keysOnly,
|
||||
CountOnly: op.countOnly,
|
||||
MinModRevision: op.minModRev,
|
||||
MaxModRevision: op.maxModRev,
|
||||
MinCreateRevision: op.minCreateRev,
|
||||
MaxCreateRevision: op.maxCreateRev,
|
||||
}
|
||||
if op.sort != nil {
|
||||
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
|
||||
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (op Op) toRequestOp() *pb.RequestOp {
|
||||
switch op.t {
|
||||
case tRange:
|
||||
r := &pb.RangeRequest{
|
||||
Key: op.key,
|
||||
RangeEnd: op.end,
|
||||
Limit: op.limit,
|
||||
Revision: op.rev,
|
||||
Serializable: op.serializable,
|
||||
KeysOnly: op.keysOnly,
|
||||
CountOnly: op.countOnly,
|
||||
}
|
||||
if op.sort != nil {
|
||||
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
|
||||
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
|
||||
}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: r}}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: op.toRangeRequest()}}
|
||||
case tPut:
|
||||
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID)}
|
||||
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
|
||||
case tDeleteRange:
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end}
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
|
||||
default:
|
||||
panic("Unknown Op")
|
||||
@@ -109,6 +130,14 @@ func OpDelete(key string, opts ...OpOption) Op {
|
||||
panic("unexpected serializable in delete")
|
||||
case ret.countOnly:
|
||||
panic("unexpected countOnly in delete")
|
||||
case ret.minModRev != 0, ret.maxModRev != 0:
|
||||
panic("unexpected mod revision filter in delete")
|
||||
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
|
||||
panic("unexpected create revision filter in delete")
|
||||
case ret.filterDelete, ret.filterPut:
|
||||
panic("unexpected filter in delete")
|
||||
case ret.createdNotify:
|
||||
panic("unexpected createdNotify in delete")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -128,7 +157,15 @@ func OpPut(key, val string, opts ...OpOption) Op {
|
||||
case ret.serializable:
|
||||
panic("unexpected serializable in put")
|
||||
case ret.countOnly:
|
||||
panic("unexpected countOnly in delete")
|
||||
panic("unexpected countOnly in put")
|
||||
case ret.minModRev != 0, ret.maxModRev != 0:
|
||||
panic("unexpected mod revision filter in put")
|
||||
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
|
||||
panic("unexpected create revision filter in put")
|
||||
case ret.filterDelete, ret.filterPut:
|
||||
panic("unexpected filter in put")
|
||||
case ret.createdNotify:
|
||||
panic("unexpected createdNotify in put")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -146,7 +183,11 @@ func opWatch(key string, opts ...OpOption) Op {
|
||||
case ret.serializable:
|
||||
panic("unexpected serializable in watch")
|
||||
case ret.countOnly:
|
||||
panic("unexpected countOnly in delete")
|
||||
panic("unexpected countOnly in watch")
|
||||
case ret.minModRev != 0, ret.maxModRev != 0:
|
||||
panic("unexpected mod revision filter in watch")
|
||||
case ret.minCreateRev != 0, ret.maxCreateRev != 0:
|
||||
panic("unexpected create revision filter in watch")
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -178,6 +219,14 @@ func WithRev(rev int64) OpOption { return func(op *Op) { op.rev = rev } }
|
||||
// 'order' can be either 'SortNone', 'SortAscend', 'SortDescend'.
|
||||
func WithSort(target SortTarget, order SortOrder) OpOption {
|
||||
return func(op *Op) {
|
||||
if target == SortByKey && order == SortAscend {
|
||||
// If order != SortNone, server fetches the entire key-space,
|
||||
// and then applies the sort and limit, if provided.
|
||||
// Since current mvcc.Range implementation returns results
|
||||
// sorted by keys in lexiographically ascending order,
|
||||
// client should ignore SortOrder if the target is SortByKey.
|
||||
order = SortNone
|
||||
}
|
||||
op.sort = &SortOption{target, order}
|
||||
}
|
||||
}
|
||||
@@ -241,6 +290,18 @@ func WithCountOnly() OpOption {
|
||||
return func(op *Op) { op.countOnly = true }
|
||||
}
|
||||
|
||||
// WithMinModRev filters out keys for Get with modification revisions less than the given revision.
|
||||
func WithMinModRev(rev int64) OpOption { return func(op *Op) { op.minModRev = rev } }
|
||||
|
||||
// WithMaxModRev filters out keys for Get with modification revisions greater than the given revision.
|
||||
func WithMaxModRev(rev int64) OpOption { return func(op *Op) { op.maxModRev = rev } }
|
||||
|
||||
// WithMinCreateRev filters out keys for Get with creation revisions less than the given revision.
|
||||
func WithMinCreateRev(rev int64) OpOption { return func(op *Op) { op.minCreateRev = rev } }
|
||||
|
||||
// WithMaxCreateRev filters out keys for Get with creation revisions greater than the given revision.
|
||||
func WithMaxCreateRev(rev int64) OpOption { return func(op *Op) { op.maxCreateRev = rev } }
|
||||
|
||||
// WithFirstCreate gets the key with the oldest creation revision in the request range.
|
||||
func WithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }
|
||||
|
||||
@@ -264,10 +325,65 @@ func withTop(target SortTarget, order SortOrder) []OpOption {
|
||||
return []OpOption{WithPrefix(), WithSort(target, order), WithLimit(1)}
|
||||
}
|
||||
|
||||
// WithProgressNotify makes watch server send periodic progress updates.
|
||||
// WithProgressNotify makes watch server send periodic progress updates
|
||||
// every 10 minutes when there is no incoming events.
|
||||
// Progress updates have zero events in WatchResponse.
|
||||
func WithProgressNotify() OpOption {
|
||||
return func(op *Op) {
|
||||
op.progressNotify = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithCreatedNotify makes watch server sends the created event.
|
||||
func WithCreatedNotify() OpOption {
|
||||
return func(op *Op) {
|
||||
op.createdNotify = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithFilterPut discards PUT events from the watcher.
|
||||
func WithFilterPut() OpOption {
|
||||
return func(op *Op) { op.filterPut = true }
|
||||
}
|
||||
|
||||
// WithFilterDelete discards DELETE events from the watcher.
|
||||
func WithFilterDelete() OpOption {
|
||||
return func(op *Op) { op.filterDelete = true }
|
||||
}
|
||||
|
||||
// WithPrevKV gets the previous key-value pair before the event happens. If the previous KV is already compacted,
|
||||
// nothing will be returned.
|
||||
func WithPrevKV() OpOption {
|
||||
return func(op *Op) {
|
||||
op.prevKV = true
|
||||
}
|
||||
}
|
||||
|
||||
// LeaseOp represents an Operation that lease can execute.
|
||||
type LeaseOp struct {
|
||||
id LeaseID
|
||||
|
||||
// for TimeToLive
|
||||
attachedKeys bool
|
||||
}
|
||||
|
||||
// LeaseOption configures lease operations.
|
||||
type LeaseOption func(*LeaseOp)
|
||||
|
||||
func (op *LeaseOp) applyOpts(opts []LeaseOption) {
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
}
|
||||
|
||||
// WithAttachedKeys requests lease timetolive API to return
|
||||
// attached keys of given lease ID.
|
||||
func WithAttachedKeys() LeaseOption {
|
||||
return func(op *LeaseOp) { op.attachedKeys = true }
|
||||
}
|
||||
|
||||
func toLeaseTimeToLiveRequest(id LeaseID, opts ...LeaseOption) *pb.LeaseTimeToLiveRequest {
|
||||
ret := &LeaseOp{id: id}
|
||||
ret.applyOpts(opts)
|
||||
return &pb.LeaseTimeToLiveRequest{ID: int64(id), Keys: ret.attachedKeys}
|
||||
}
|
||||
|
||||
+12
-2
@@ -15,9 +15,11 @@
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
type rpcFunc func(ctx context.Context) error
|
||||
@@ -27,8 +29,16 @@ func (c *Client) newRetryWrapper() retryRpcFunc {
|
||||
return func(rpcCtx context.Context, f rpcFunc) {
|
||||
for {
|
||||
err := f(rpcCtx)
|
||||
// ignore grpc conn closing on fail-fast calls; they are transient errors
|
||||
if err == nil || !isConnClosing(err) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
// only retry if unavailable
|
||||
if grpc.Code(err) != codes.Unavailable {
|
||||
return
|
||||
}
|
||||
// always stop retry on etcd errors
|
||||
eErr := rpctypes.Error(err)
|
||||
if _, ok := eErr.(rpctypes.EtcdError); ok {
|
||||
return
|
||||
}
|
||||
select {
|
||||
|
||||
+7
-1
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Txn is the interface that wraps mini-transactions.
|
||||
@@ -152,7 +153,12 @@ func (txn *txn) Commit() (*TxnResponse, error) {
|
||||
|
||||
func (txn *txn) commit() (*TxnResponse, error) {
|
||||
r := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
|
||||
resp, err := txn.kv.remote.Txn(txn.ctx, r)
|
||||
|
||||
var opts []grpc.CallOption
|
||||
if !txn.isWrite {
|
||||
opts = []grpc.CallOption{grpc.FailFast(false)}
|
||||
}
|
||||
resp, err := txn.kv.remote.Txn(txn.ctx, r, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+291
-299
@@ -61,6 +61,9 @@ type WatchResponse struct {
|
||||
// the channel sends a final response that has Canceled set to true with a non-nil Err().
|
||||
Canceled bool
|
||||
|
||||
// Created is used to indicate the creation of the watcher.
|
||||
Created bool
|
||||
|
||||
closeErr error
|
||||
}
|
||||
|
||||
@@ -89,7 +92,7 @@ func (wr *WatchResponse) Err() error {
|
||||
|
||||
// IsProgressNotify returns true if the WatchResponse is progress notification.
|
||||
func (wr *WatchResponse) IsProgressNotify() bool {
|
||||
return len(wr.Events) == 0 && !wr.Canceled
|
||||
return len(wr.Events) == 0 && !wr.Canceled && !wr.Created && wr.CompactRevision == 0 && wr.Header.Revision != 0
|
||||
}
|
||||
|
||||
// watcher implements the Watcher interface
|
||||
@@ -98,10 +101,12 @@ type watcher struct {
|
||||
|
||||
// mu protects the grpc streams map
|
||||
mu sync.RWMutex
|
||||
|
||||
// streams holds all the active grpc streams keyed by ctx value.
|
||||
streams map[string]*watchGrpcStream
|
||||
}
|
||||
|
||||
// watchGrpcStream tracks all watch resources attached to a single grpc stream.
|
||||
type watchGrpcStream struct {
|
||||
owner *watcher
|
||||
remote pb.WatchClient
|
||||
@@ -112,10 +117,10 @@ type watchGrpcStream struct {
|
||||
ctxKey string
|
||||
cancel context.CancelFunc
|
||||
|
||||
// mu protects the streams map
|
||||
mu sync.RWMutex
|
||||
// streams holds all active watchers
|
||||
streams map[int64]*watcherStream
|
||||
// substreams holds all active watchers on this grpc stream
|
||||
substreams map[int64]*watcherStream
|
||||
// resuming holds all resuming watchers on this grpc stream
|
||||
resuming []*watcherStream
|
||||
|
||||
// reqc sends a watch request from Watch() to the main goroutine
|
||||
reqc chan *watchRequest
|
||||
@@ -127,8 +132,12 @@ type watchGrpcStream struct {
|
||||
donec chan struct{}
|
||||
// errc transmits errors from grpc Recv to the watch stream reconn logic
|
||||
errc chan error
|
||||
// closingc gets the watcherStream of closing watchers
|
||||
closingc chan *watcherStream
|
||||
|
||||
// the error that closed the watch stream
|
||||
// resumec closes to signal that all substreams should begin resuming
|
||||
resumec chan struct{}
|
||||
// closeErr is the error that closed the watch stream
|
||||
closeErr error
|
||||
}
|
||||
|
||||
@@ -138,8 +147,14 @@ type watchRequest struct {
|
||||
key string
|
||||
end string
|
||||
rev int64
|
||||
// progressNotify is for progress updates.
|
||||
// send created notification event if this field is true
|
||||
createdNotify bool
|
||||
// progressNotify is for progress updates
|
||||
progressNotify bool
|
||||
// filters is the list of events to filter out
|
||||
filters []pb.WatchCreateRequest_FilterType
|
||||
// get the previous key-value pair before the event happens
|
||||
prevKV bool
|
||||
// retc receives a chan WatchResponse once the watcher is established
|
||||
retc chan chan WatchResponse
|
||||
}
|
||||
@@ -150,20 +165,27 @@ type watcherStream struct {
|
||||
initReq watchRequest
|
||||
|
||||
// outc publishes watch responses to subscriber
|
||||
outc chan<- WatchResponse
|
||||
outc chan WatchResponse
|
||||
// recvc buffers watch responses before publishing
|
||||
recvc chan *WatchResponse
|
||||
id int64
|
||||
// donec closes when the watcherStream goroutine stops.
|
||||
donec chan struct{}
|
||||
// closing is set to true when stream should be scheduled to shutdown.
|
||||
closing bool
|
||||
// id is the registered watch id on the grpc stream
|
||||
id int64
|
||||
|
||||
// lastRev is revision last successfully sent over outc
|
||||
lastRev int64
|
||||
// resumec indicates the stream must recover at a given revision
|
||||
resumec chan int64
|
||||
// buf holds all events received from etcd but not yet consumed by the client
|
||||
buf []*WatchResponse
|
||||
}
|
||||
|
||||
func NewWatcher(c *Client) Watcher {
|
||||
return NewWatchFromWatchClient(pb.NewWatchClient(c.conn))
|
||||
}
|
||||
|
||||
func NewWatchFromWatchClient(wc pb.WatchClient) Watcher {
|
||||
return &watcher{
|
||||
remote: pb.NewWatchClient(c.conn),
|
||||
remote: wc,
|
||||
streams: make(map[string]*watchGrpcStream),
|
||||
}
|
||||
}
|
||||
@@ -182,18 +204,20 @@ func (vc *valCtx) Err() error { return nil }
|
||||
func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
|
||||
ctx, cancel := context.WithCancel(&valCtx{inctx})
|
||||
wgs := &watchGrpcStream{
|
||||
owner: w,
|
||||
remote: w.remote,
|
||||
ctx: ctx,
|
||||
ctxKey: fmt.Sprintf("%v", inctx),
|
||||
cancel: cancel,
|
||||
streams: make(map[int64]*watcherStream),
|
||||
owner: w,
|
||||
remote: w.remote,
|
||||
ctx: ctx,
|
||||
ctxKey: fmt.Sprintf("%v", inctx),
|
||||
cancel: cancel,
|
||||
substreams: make(map[int64]*watcherStream),
|
||||
|
||||
respc: make(chan *pb.WatchResponse),
|
||||
reqc: make(chan *watchRequest),
|
||||
stopc: make(chan struct{}),
|
||||
donec: make(chan struct{}),
|
||||
errc: make(chan error, 1),
|
||||
respc: make(chan *pb.WatchResponse),
|
||||
reqc: make(chan *watchRequest),
|
||||
stopc: make(chan struct{}),
|
||||
donec: make(chan struct{}),
|
||||
errc: make(chan error, 1),
|
||||
closingc: make(chan *watcherStream),
|
||||
resumec: make(chan struct{}),
|
||||
}
|
||||
go wgs.run()
|
||||
return wgs
|
||||
@@ -203,14 +227,24 @@ func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
|
||||
func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan {
|
||||
ow := opWatch(key, opts...)
|
||||
|
||||
retc := make(chan chan WatchResponse, 1)
|
||||
var filters []pb.WatchCreateRequest_FilterType
|
||||
if ow.filterPut {
|
||||
filters = append(filters, pb.WatchCreateRequest_NOPUT)
|
||||
}
|
||||
if ow.filterDelete {
|
||||
filters = append(filters, pb.WatchCreateRequest_NODELETE)
|
||||
}
|
||||
|
||||
wr := &watchRequest{
|
||||
ctx: ctx,
|
||||
createdNotify: ow.createdNotify,
|
||||
key: string(ow.key),
|
||||
end: string(ow.end),
|
||||
rev: ow.rev,
|
||||
progressNotify: ow.progressNotify,
|
||||
retc: retc,
|
||||
filters: filters,
|
||||
prevKV: ow.prevKV,
|
||||
retc: make(chan chan WatchResponse, 1),
|
||||
}
|
||||
|
||||
ok := false
|
||||
@@ -242,7 +276,6 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
|
||||
case reqc <- wr:
|
||||
ok = true
|
||||
case <-wr.ctx.Done():
|
||||
wgs.stopIfEmpty()
|
||||
case <-donec:
|
||||
if wgs.closeErr != nil {
|
||||
closeCh <- WatchResponse{closeErr: wgs.closeErr}
|
||||
@@ -255,7 +288,7 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
|
||||
// receive channel
|
||||
if ok {
|
||||
select {
|
||||
case ret := <-retc:
|
||||
case ret := <-wr.retc:
|
||||
return ret
|
||||
case <-ctx.Done():
|
||||
case <-donec:
|
||||
@@ -286,12 +319,7 @@ func (w *watcher) Close() (err error) {
|
||||
}
|
||||
|
||||
func (w *watchGrpcStream) Close() (err error) {
|
||||
w.mu.Lock()
|
||||
if w.stopc != nil {
|
||||
close(w.stopc)
|
||||
w.stopc = nil
|
||||
}
|
||||
w.mu.Unlock()
|
||||
close(w.stopc)
|
||||
<-w.donec
|
||||
select {
|
||||
case err = <-w.errc:
|
||||
@@ -300,67 +328,57 @@ func (w *watchGrpcStream) Close() (err error) {
|
||||
return toErr(w.ctx, err)
|
||||
}
|
||||
|
||||
func (w *watchGrpcStream) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) {
|
||||
if pendingReq == nil {
|
||||
// no pending request; ignore
|
||||
return
|
||||
}
|
||||
if resp.Canceled || resp.CompactRevision != 0 {
|
||||
// a cancel at id creation time means the start revision has
|
||||
// been compacted out of the store
|
||||
ret := make(chan WatchResponse, 1)
|
||||
ret <- WatchResponse{
|
||||
Header: *resp.Header,
|
||||
CompactRevision: resp.CompactRevision,
|
||||
Canceled: true}
|
||||
close(ret)
|
||||
pendingReq.retc <- ret
|
||||
return
|
||||
}
|
||||
|
||||
ret := make(chan WatchResponse)
|
||||
if resp.WatchId == -1 {
|
||||
// failed; no channel
|
||||
close(ret)
|
||||
pendingReq.retc <- ret
|
||||
return
|
||||
}
|
||||
|
||||
ws := &watcherStream{
|
||||
initReq: *pendingReq,
|
||||
id: resp.WatchId,
|
||||
outc: ret,
|
||||
// buffered so unlikely to block on sending while holding mu
|
||||
recvc: make(chan *WatchResponse, 4),
|
||||
resumec: make(chan int64),
|
||||
}
|
||||
|
||||
if pendingReq.rev == 0 {
|
||||
// note the header revision so that a put following a current watcher
|
||||
// disconnect will arrive on the watcher channel after reconnect
|
||||
ws.initReq.rev = resp.Header.Revision
|
||||
}
|
||||
|
||||
func (w *watcher) closeStream(wgs *watchGrpcStream) {
|
||||
w.mu.Lock()
|
||||
w.streams[ws.id] = ws
|
||||
close(wgs.donec)
|
||||
wgs.cancel()
|
||||
if w.streams != nil {
|
||||
delete(w.streams, wgs.ctxKey)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
// pass back the subscriber channel for the watcher
|
||||
pendingReq.retc <- ret
|
||||
|
||||
// send messages to subscriber
|
||||
go w.serveStream(ws)
|
||||
}
|
||||
|
||||
// closeStream closes the watcher resources and removes it
|
||||
func (w *watchGrpcStream) closeStream(ws *watcherStream) {
|
||||
w.mu.Lock()
|
||||
// cancels request stream; subscriber receives nil channel
|
||||
close(ws.initReq.retc)
|
||||
// close subscriber's channel
|
||||
func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
|
||||
if resp.WatchId == -1 {
|
||||
// failed; no channel
|
||||
close(ws.recvc)
|
||||
return
|
||||
}
|
||||
ws.id = resp.WatchId
|
||||
w.substreams[ws.id] = ws
|
||||
}
|
||||
|
||||
func (w *watchGrpcStream) sendCloseSubstream(ws *watcherStream, resp *WatchResponse) {
|
||||
select {
|
||||
case ws.outc <- *resp:
|
||||
case <-ws.initReq.ctx.Done():
|
||||
case <-time.After(closeSendErrTimeout):
|
||||
}
|
||||
close(ws.outc)
|
||||
delete(w.streams, ws.id)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *watchGrpcStream) closeSubstream(ws *watcherStream) {
|
||||
// send channel response in case stream was never established
|
||||
select {
|
||||
case ws.initReq.retc <- ws.outc:
|
||||
default:
|
||||
}
|
||||
// close subscriber's channel
|
||||
if closeErr := w.closeErr; closeErr != nil && ws.initReq.ctx.Err() == nil {
|
||||
go w.sendCloseSubstream(ws, &WatchResponse{closeErr: w.closeErr})
|
||||
} else {
|
||||
close(ws.outc)
|
||||
}
|
||||
if ws.id != -1 {
|
||||
delete(w.substreams, ws.id)
|
||||
return
|
||||
}
|
||||
for i := range w.resuming {
|
||||
if w.resuming[i] == ws {
|
||||
w.resuming[i] = nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run is the root of the goroutines for managing a watcher client
|
||||
@@ -368,66 +386,79 @@ func (w *watchGrpcStream) run() {
|
||||
var wc pb.Watch_WatchClient
|
||||
var closeErr error
|
||||
|
||||
defer func() {
|
||||
w.owner.mu.Lock()
|
||||
w.closeErr = closeErr
|
||||
if w.owner.streams != nil {
|
||||
delete(w.owner.streams, w.ctxKey)
|
||||
}
|
||||
close(w.donec)
|
||||
w.owner.mu.Unlock()
|
||||
w.cancel()
|
||||
}()
|
||||
// substreams marked to close but goroutine still running; needed for
|
||||
// avoiding double-closing recvc on grpc stream teardown
|
||||
closing := make(map[*watcherStream]struct{})
|
||||
|
||||
// already stopped?
|
||||
w.mu.RLock()
|
||||
stopc := w.stopc
|
||||
w.mu.RUnlock()
|
||||
if stopc == nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
w.closeErr = closeErr
|
||||
// shutdown substreams and resuming substreams
|
||||
for _, ws := range w.substreams {
|
||||
if _, ok := closing[ws]; !ok {
|
||||
close(ws.recvc)
|
||||
}
|
||||
}
|
||||
for _, ws := range w.resuming {
|
||||
if _, ok := closing[ws]; ws != nil && !ok {
|
||||
close(ws.recvc)
|
||||
}
|
||||
}
|
||||
w.joinSubstreams()
|
||||
for toClose := len(w.substreams) + len(w.resuming); toClose > 0; toClose-- {
|
||||
w.closeSubstream(<-w.closingc)
|
||||
}
|
||||
|
||||
w.owner.closeStream(w)
|
||||
}()
|
||||
|
||||
// start a stream with the etcd grpc server
|
||||
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pendingReq, failedReq *watchRequest
|
||||
curReqC := w.reqc
|
||||
cancelSet := make(map[int64]struct{})
|
||||
|
||||
for {
|
||||
select {
|
||||
// Watch() requested
|
||||
case pendingReq = <-curReqC:
|
||||
// no more watch requests until there's a response
|
||||
curReqC = nil
|
||||
if err := wc.Send(pendingReq.toPB()); err == nil {
|
||||
// pendingReq now waits on w.respc
|
||||
break
|
||||
case wreq := <-w.reqc:
|
||||
outc := make(chan WatchResponse, 1)
|
||||
ws := &watcherStream{
|
||||
initReq: *wreq,
|
||||
id: -1,
|
||||
outc: outc,
|
||||
// unbufffered so resumes won't cause repeat events
|
||||
recvc: make(chan *WatchResponse),
|
||||
}
|
||||
|
||||
ws.donec = make(chan struct{})
|
||||
go w.serveSubstream(ws, w.resumec)
|
||||
|
||||
// queue up for watcher creation/resume
|
||||
w.resuming = append(w.resuming, ws)
|
||||
if len(w.resuming) == 1 {
|
||||
// head of resume queue, can register a new watcher
|
||||
wc.Send(ws.initReq.toPB())
|
||||
}
|
||||
failedReq = pendingReq
|
||||
// New events from the watch client
|
||||
case pbresp := <-w.respc:
|
||||
switch {
|
||||
case pbresp.Created:
|
||||
// response to pending req, try to add
|
||||
w.addStream(pbresp, pendingReq)
|
||||
pendingReq = nil
|
||||
curReqC = w.reqc
|
||||
// response to head of queue creation
|
||||
if ws := w.resuming[0]; ws != nil {
|
||||
w.addSubstream(pbresp, ws)
|
||||
w.dispatchEvent(pbresp)
|
||||
w.resuming[0] = nil
|
||||
}
|
||||
if ws := w.nextResume(); ws != nil {
|
||||
wc.Send(ws.initReq.toPB())
|
||||
}
|
||||
case pbresp.Canceled:
|
||||
delete(cancelSet, pbresp.WatchId)
|
||||
// shutdown serveStream, if any
|
||||
w.mu.Lock()
|
||||
if ws, ok := w.streams[pbresp.WatchId]; ok {
|
||||
if ws, ok := w.substreams[pbresp.WatchId]; ok {
|
||||
// signal to stream goroutine to update closingc
|
||||
close(ws.recvc)
|
||||
delete(w.streams, ws.id)
|
||||
}
|
||||
numStreams := len(w.streams)
|
||||
w.mu.Unlock()
|
||||
if numStreams == 0 {
|
||||
// don't leak watcher streams
|
||||
return
|
||||
closing[ws] = struct{}{}
|
||||
}
|
||||
default:
|
||||
// dispatch to appropriate watch stream
|
||||
@@ -448,57 +479,66 @@ func (w *watchGrpcStream) run() {
|
||||
wc.Send(req)
|
||||
}
|
||||
// watch client failed to recv; spawn another if possible
|
||||
// TODO report watch client errors from errc?
|
||||
case err := <-w.errc:
|
||||
if toErr(w.ctx, err) == v3rpc.ErrNoLeader {
|
||||
if isHaltErr(w.ctx, err) || toErr(w.ctx, err) == v3rpc.ErrNoLeader {
|
||||
closeErr = err
|
||||
return
|
||||
}
|
||||
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||
return
|
||||
}
|
||||
curReqC = w.reqc
|
||||
if pendingReq != nil {
|
||||
failedReq = pendingReq
|
||||
if ws := w.nextResume(); ws != nil {
|
||||
wc.Send(ws.initReq.toPB())
|
||||
}
|
||||
cancelSet = make(map[int64]struct{})
|
||||
case <-stopc:
|
||||
case <-w.stopc:
|
||||
return
|
||||
}
|
||||
|
||||
// send failed; queue for retry
|
||||
if failedReq != nil {
|
||||
go func(wr *watchRequest) {
|
||||
select {
|
||||
case w.reqc <- wr:
|
||||
case <-wr.ctx.Done():
|
||||
case <-w.donec:
|
||||
}
|
||||
}(pendingReq)
|
||||
failedReq = nil
|
||||
pendingReq = nil
|
||||
case ws := <-w.closingc:
|
||||
w.closeSubstream(ws)
|
||||
delete(closing, ws)
|
||||
if len(w.substreams)+len(w.resuming) == 0 {
|
||||
// no more watchers on this stream, shutdown
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nextResume chooses the next resuming to register with the grpc stream. Abandoned
|
||||
// streams are marked as nil in the queue since the head must wait for its inflight registration.
|
||||
func (w *watchGrpcStream) nextResume() *watcherStream {
|
||||
for len(w.resuming) != 0 {
|
||||
if w.resuming[0] != nil {
|
||||
return w.resuming[0]
|
||||
}
|
||||
w.resuming = w.resuming[1:len(w.resuming)]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dispatchEvent sends a WatchResponse to the appropriate watcher stream
|
||||
func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
ws, ok := w.streams[pbresp.WatchId]
|
||||
ws, ok := w.substreams[pbresp.WatchId]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
events := make([]*Event, len(pbresp.Events))
|
||||
for i, ev := range pbresp.Events {
|
||||
events[i] = (*Event)(ev)
|
||||
}
|
||||
if ok {
|
||||
wr := &WatchResponse{
|
||||
Header: *pbresp.Header,
|
||||
Events: events,
|
||||
CompactRevision: pbresp.CompactRevision,
|
||||
Canceled: pbresp.Canceled}
|
||||
ws.recvc <- wr
|
||||
wr := &WatchResponse{
|
||||
Header: *pbresp.Header,
|
||||
Events: events,
|
||||
CompactRevision: pbresp.CompactRevision,
|
||||
Created: pbresp.Created,
|
||||
Canceled: pbresp.Canceled,
|
||||
}
|
||||
return ok
|
||||
select {
|
||||
case ws.recvc <- wr:
|
||||
case <-ws.donec:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// serveWatchClient forwards messages from the grpc stream to run()
|
||||
@@ -520,134 +560,126 @@ 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{}
|
||||
// serveSubstream forwards watch responses from run() to the subscriber
|
||||
func (w *watchGrpcStream) serveSubstream(ws *watcherStream, resumec chan struct{}) {
|
||||
if ws.closing {
|
||||
panic("created substream goroutine but substream is closing")
|
||||
}
|
||||
|
||||
// nextRev is the minimum expected next revision
|
||||
nextRev := ws.initReq.rev
|
||||
resuming := false
|
||||
closing := false
|
||||
for !closing {
|
||||
defer func() {
|
||||
if !resuming {
|
||||
ws.closing = true
|
||||
}
|
||||
close(ws.donec)
|
||||
if !resuming {
|
||||
w.closingc <- ws
|
||||
}
|
||||
}()
|
||||
|
||||
emptyWr := &WatchResponse{}
|
||||
for {
|
||||
curWr := emptyWr
|
||||
outc := ws.outc
|
||||
if len(wrs) > 0 {
|
||||
curWr = wrs[0]
|
||||
|
||||
if len(ws.buf) > 0 && ws.buf[0].Created {
|
||||
select {
|
||||
case ws.initReq.retc <- ws.outc:
|
||||
// send first creation event and only if requested
|
||||
if !ws.initReq.createdNotify {
|
||||
ws.buf = ws.buf[1:]
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if len(ws.buf) > 0 {
|
||||
curWr = ws.buf[0]
|
||||
} else {
|
||||
outc = nil
|
||||
}
|
||||
select {
|
||||
case outc <- *curWr:
|
||||
if wrs[0].Err() != nil {
|
||||
closing = true
|
||||
break
|
||||
}
|
||||
var newRev int64
|
||||
if len(wrs[0].Events) > 0 {
|
||||
newRev = wrs[0].Events[len(wrs[0].Events)-1].Kv.ModRevision
|
||||
} else {
|
||||
newRev = wrs[0].Header.Revision
|
||||
}
|
||||
if newRev != ws.lastRev {
|
||||
ws.lastRev = newRev
|
||||
}
|
||||
wrs[0] = nil
|
||||
wrs = wrs[1:]
|
||||
case wr, ok := <-ws.recvc:
|
||||
if !ok {
|
||||
// shutdown from closeStream
|
||||
if ws.buf[0].Err() != nil {
|
||||
return
|
||||
}
|
||||
// resume up to last seen event if disconnected
|
||||
if resuming && wr.Err() == nil {
|
||||
resuming = false
|
||||
// trim events already seen
|
||||
for i := 0; i < len(wr.Events); i++ {
|
||||
if wr.Events[i].Kv.ModRevision > ws.lastRev {
|
||||
wr.Events = wr.Events[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
// only forward new events
|
||||
if wr.Events[0].Kv.ModRevision == ws.lastRev {
|
||||
break
|
||||
}
|
||||
ws.buf[0] = nil
|
||||
ws.buf = ws.buf[1:]
|
||||
case wr, ok := <-ws.recvc:
|
||||
if !ok {
|
||||
// shutdown from closeSubstream
|
||||
return
|
||||
}
|
||||
resuming = false
|
||||
// TODO don't keep buffering if subscriber stops reading
|
||||
wrs = append(wrs, wr)
|
||||
case resumeRev := <-ws.resumec:
|
||||
wrs = nil
|
||||
resuming = true
|
||||
if resumeRev == -1 {
|
||||
// pause serving stream while resume gets set up
|
||||
break
|
||||
// TODO pause channel if buffer gets too large
|
||||
ws.buf = append(ws.buf, wr)
|
||||
nextRev = wr.Header.Revision
|
||||
if len(wr.Events) > 0 {
|
||||
nextRev = wr.Events[len(wr.Events)-1].Kv.ModRevision + 1
|
||||
}
|
||||
if resumeRev != ws.lastRev {
|
||||
panic("unexpected resume revision")
|
||||
}
|
||||
case <-w.donec:
|
||||
closing = true
|
||||
closeErr = w.closeErr
|
||||
ws.initReq.rev = nextRev
|
||||
case <-ws.initReq.ctx.Done():
|
||||
closing = true
|
||||
return
|
||||
case <-resumec:
|
||||
resuming = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// try to send off close error
|
||||
if closeErr != nil {
|
||||
select {
|
||||
case ws.outc <- WatchResponse{closeErr: w.closeErr}:
|
||||
case <-w.donec:
|
||||
case <-time.After(closeSendErrTimeout):
|
||||
}
|
||||
}
|
||||
|
||||
w.closeStream(ws)
|
||||
w.stopIfEmpty()
|
||||
// lazily send cancel message if events on missing id
|
||||
}
|
||||
|
||||
func (wgs *watchGrpcStream) stopIfEmpty() {
|
||||
wgs.mu.Lock()
|
||||
if len(wgs.streams) == 0 && wgs.stopc != nil {
|
||||
close(wgs.stopc)
|
||||
wgs.stopc = nil
|
||||
}
|
||||
wgs.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
|
||||
ws, rerr := w.resume()
|
||||
if rerr != nil {
|
||||
return nil, rerr
|
||||
// connect to grpc stream
|
||||
wc, err := w.openWatchClient()
|
||||
if err != nil {
|
||||
return nil, v3rpc.Error(err)
|
||||
}
|
||||
go w.serveWatchClient(ws)
|
||||
return ws, nil
|
||||
// mark all substreams as resuming
|
||||
if len(w.substreams)+len(w.resuming) > 0 {
|
||||
close(w.resumec)
|
||||
w.resumec = make(chan struct{})
|
||||
w.joinSubstreams()
|
||||
for _, ws := range w.substreams {
|
||||
ws.id = -1
|
||||
w.resuming = append(w.resuming, ws)
|
||||
}
|
||||
for _, ws := range w.resuming {
|
||||
if ws == nil || ws.closing {
|
||||
continue
|
||||
}
|
||||
ws.donec = make(chan struct{})
|
||||
go w.serveSubstream(ws, w.resumec)
|
||||
}
|
||||
}
|
||||
w.substreams = make(map[int64]*watcherStream)
|
||||
// receive data from new grpc stream
|
||||
go w.serveWatchClient(wc)
|
||||
return wc, nil
|
||||
}
|
||||
|
||||
// resume creates a new WatchClient with all current watchers reestablished
|
||||
func (w *watchGrpcStream) resume() (ws pb.Watch_WatchClient, err error) {
|
||||
for {
|
||||
if ws, err = w.openWatchClient(); err != nil {
|
||||
break
|
||||
} else if err = w.resumeWatchers(ws); err == nil {
|
||||
break
|
||||
// joinSubstream waits for all substream goroutines to complete
|
||||
func (w *watchGrpcStream) joinSubstreams() {
|
||||
for _, ws := range w.substreams {
|
||||
<-ws.donec
|
||||
}
|
||||
for _, ws := range w.resuming {
|
||||
if ws != nil {
|
||||
<-ws.donec
|
||||
}
|
||||
}
|
||||
return ws, v3rpc.Error(err)
|
||||
}
|
||||
|
||||
// openWatchClient retries opening a watchclient until retryConnection fails
|
||||
func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) {
|
||||
for {
|
||||
w.mu.Lock()
|
||||
stopc := w.stopc
|
||||
w.mu.Unlock()
|
||||
if stopc == nil {
|
||||
select {
|
||||
case <-w.stopc:
|
||||
if err == nil {
|
||||
err = context.Canceled
|
||||
return nil, context.Canceled
|
||||
}
|
||||
return nil, err
|
||||
default:
|
||||
}
|
||||
if ws, err = w.remote.Watch(w.ctx, grpc.FailFast(false)); ws != nil && err == nil {
|
||||
break
|
||||
@@ -659,48 +691,6 @@ func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error)
|
||||
return ws, nil
|
||||
}
|
||||
|
||||
// resumeWatchers rebuilds every registered watcher on a new client
|
||||
func (w *watchGrpcStream) resumeWatchers(wc pb.Watch_WatchClient) error {
|
||||
w.mu.RLock()
|
||||
streams := make([]*watcherStream, 0, len(w.streams))
|
||||
for _, ws := range w.streams {
|
||||
streams = append(streams, ws)
|
||||
}
|
||||
w.mu.RUnlock()
|
||||
|
||||
for _, ws := range streams {
|
||||
// pause serveStream
|
||||
ws.resumec <- -1
|
||||
|
||||
// reconstruct watcher from initial request
|
||||
if ws.lastRev != 0 {
|
||||
ws.initReq.rev = ws.lastRev
|
||||
}
|
||||
if err := wc.Send(ws.initReq.toPB()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// wait for request ack
|
||||
resp, err := wc.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(resp.Events) != 0 || !resp.Created {
|
||||
return fmt.Errorf("watcher: unexpected response (%+v)", resp)
|
||||
}
|
||||
|
||||
// id may be different since new remote watcher; update map
|
||||
w.mu.Lock()
|
||||
delete(w.streams, ws.id)
|
||||
ws.id = resp.WatchId
|
||||
w.streams[ws.id] = ws
|
||||
w.mu.Unlock()
|
||||
|
||||
// unpause serveStream
|
||||
ws.resumec <- ws.lastRev
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// toPB converts an internal watch request structure to its protobuf messagefunc (wr *watchRequest)
|
||||
func (wr *watchRequest) toPB() *pb.WatchRequest {
|
||||
req := &pb.WatchCreateRequest{
|
||||
@@ -708,6 +698,8 @@ func (wr *watchRequest) toPB() *pb.WatchRequest {
|
||||
Key: []byte(wr.key),
|
||||
RangeEnd: []byte(wr.end),
|
||||
ProgressNotify: wr.progressNotify,
|
||||
Filters: wr.filters,
|
||||
PrevKv: wr.prevKV,
|
||||
}
|
||||
cr := &pb.WatchRequest_CreateRequest{CreateRequest: req}
|
||||
return &pb.WatchRequest{RequestUnion: cr}
|
||||
|
||||
+27
-12
@@ -36,7 +36,8 @@ var (
|
||||
ErrGRPCMemberBadURLs = grpc.Errorf(codes.InvalidArgument, "etcdserver: given member URLs are invalid")
|
||||
ErrGRPCMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")
|
||||
|
||||
ErrGRPCRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
|
||||
ErrGRPCRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
|
||||
ErrGRPCRequestTooManyRequests = grpc.Errorf(codes.ResourceExhausted, "etcdserver: too many requests")
|
||||
|
||||
ErrGRPCRootUserNotExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not exist")
|
||||
ErrGRPCRootRoleNotExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not have root role")
|
||||
@@ -45,13 +46,17 @@ var (
|
||||
ErrGRPCRoleAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
|
||||
ErrGRPCRoleNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name not found")
|
||||
ErrGRPCAuthFailed = grpc.Errorf(codes.InvalidArgument, "etcdserver: authentication failed, invalid user ID or password")
|
||||
ErrGRPCPermissionDenied = grpc.Errorf(codes.FailedPrecondition, "etcdserver: permission denied")
|
||||
ErrGRPCPermissionDenied = grpc.Errorf(codes.PermissionDenied, "etcdserver: permission denied")
|
||||
ErrGRPCRoleNotGranted = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role is not granted to the user")
|
||||
ErrGRPCPermissionNotGranted = grpc.Errorf(codes.FailedPrecondition, "etcdserver: permission is not granted to the role")
|
||||
ErrGRPCAuthNotEnabled = grpc.Errorf(codes.FailedPrecondition, "etcdserver: authentication is not enabled")
|
||||
|
||||
ErrGRPCNoLeader = grpc.Errorf(codes.Unavailable, "etcdserver: no leader")
|
||||
ErrGRPCNotCapable = grpc.Errorf(codes.Unavailable, "etcdserver: not capable")
|
||||
ErrGRPCStopped = grpc.Errorf(codes.Unavailable, "etcdserver: server stopped")
|
||||
ErrGRPCNoLeader = grpc.Errorf(codes.Unavailable, "etcdserver: no leader")
|
||||
ErrGRPCNotCapable = grpc.Errorf(codes.Unavailable, "etcdserver: not capable")
|
||||
ErrGRPCStopped = grpc.Errorf(codes.Unavailable, "etcdserver: server stopped")
|
||||
ErrGRPCTimeout = grpc.Errorf(codes.Unavailable, "etcdserver: request timed out")
|
||||
ErrGRPCTimeoutDueToLeaderFail = grpc.Errorf(codes.Unavailable, "etcdserver: request timed out, possibly due to previous leader failure")
|
||||
ErrGRPCUnhealthy = grpc.Errorf(codes.Unavailable, "etcdserver: unhealthy cluster")
|
||||
|
||||
errStringToError = map[string]error{
|
||||
grpc.ErrorDesc(ErrGRPCEmptyKey): ErrGRPCEmptyKey,
|
||||
@@ -69,7 +74,8 @@ var (
|
||||
grpc.ErrorDesc(ErrGRPCMemberBadURLs): ErrGRPCMemberBadURLs,
|
||||
grpc.ErrorDesc(ErrGRPCMemberNotFound): ErrGRPCMemberNotFound,
|
||||
|
||||
grpc.ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
|
||||
grpc.ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
|
||||
grpc.ErrorDesc(ErrGRPCRequestTooManyRequests): ErrGRPCRequestTooManyRequests,
|
||||
|
||||
grpc.ErrorDesc(ErrGRPCRootUserNotExist): ErrGRPCRootUserNotExist,
|
||||
grpc.ErrorDesc(ErrGRPCRootRoleNotExist): ErrGRPCRootRoleNotExist,
|
||||
@@ -81,10 +87,14 @@ var (
|
||||
grpc.ErrorDesc(ErrGRPCPermissionDenied): ErrGRPCPermissionDenied,
|
||||
grpc.ErrorDesc(ErrGRPCRoleNotGranted): ErrGRPCRoleNotGranted,
|
||||
grpc.ErrorDesc(ErrGRPCPermissionNotGranted): ErrGRPCPermissionNotGranted,
|
||||
grpc.ErrorDesc(ErrGRPCAuthNotEnabled): ErrGRPCAuthNotEnabled,
|
||||
|
||||
grpc.ErrorDesc(ErrGRPCNoLeader): ErrGRPCNoLeader,
|
||||
grpc.ErrorDesc(ErrGRPCNotCapable): ErrGRPCNotCapable,
|
||||
grpc.ErrorDesc(ErrGRPCStopped): ErrGRPCStopped,
|
||||
grpc.ErrorDesc(ErrGRPCNoLeader): ErrGRPCNoLeader,
|
||||
grpc.ErrorDesc(ErrGRPCNotCapable): ErrGRPCNotCapable,
|
||||
grpc.ErrorDesc(ErrGRPCStopped): ErrGRPCStopped,
|
||||
grpc.ErrorDesc(ErrGRPCTimeout): ErrGRPCTimeout,
|
||||
grpc.ErrorDesc(ErrGRPCTimeoutDueToLeaderFail): ErrGRPCTimeoutDueToLeaderFail,
|
||||
grpc.ErrorDesc(ErrGRPCUnhealthy): ErrGRPCUnhealthy,
|
||||
}
|
||||
|
||||
// client-side error
|
||||
@@ -104,6 +114,7 @@ var (
|
||||
ErrMemberNotFound = Error(ErrGRPCMemberNotFound)
|
||||
|
||||
ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)
|
||||
ErrTooManyRequests = Error(ErrGRPCRequestTooManyRequests)
|
||||
|
||||
ErrRootUserNotExist = Error(ErrGRPCRootUserNotExist)
|
||||
ErrRootRoleNotExist = Error(ErrGRPCRootRoleNotExist)
|
||||
@@ -115,10 +126,14 @@ var (
|
||||
ErrPermissionDenied = Error(ErrGRPCPermissionDenied)
|
||||
ErrRoleNotGranted = Error(ErrGRPCRoleNotGranted)
|
||||
ErrPermissionNotGranted = Error(ErrGRPCPermissionNotGranted)
|
||||
ErrAuthNotEnabled = Error(ErrGRPCAuthNotEnabled)
|
||||
|
||||
ErrNoLeader = Error(ErrGRPCNoLeader)
|
||||
ErrNotCapable = Error(ErrGRPCNotCapable)
|
||||
ErrStopped = Error(ErrGRPCStopped)
|
||||
ErrNoLeader = Error(ErrGRPCNoLeader)
|
||||
ErrNotCapable = Error(ErrGRPCNotCapable)
|
||||
ErrStopped = Error(ErrGRPCStopped)
|
||||
ErrTimeout = Error(ErrGRPCTimeout)
|
||||
ErrTimeoutDueToLeaderFail = Error(ErrGRPCTimeoutDueToLeaderFail)
|
||||
ErrUnhealthy = Error(ErrGRPCUnhealthy)
|
||||
)
|
||||
|
||||
// EtcdError defines gRPC server errors.
|
||||
|
||||
+4
-2
@@ -45,6 +45,8 @@
|
||||
LeaseRevokeResponse
|
||||
LeaseKeepAliveRequest
|
||||
LeaseKeepAliveResponse
|
||||
LeaseTimeToLiveRequest
|
||||
LeaseTimeToLiveResponse
|
||||
Member
|
||||
MemberAddRequest
|
||||
MemberAddResponse
|
||||
@@ -102,9 +104,9 @@ import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
|
||||
math "math"
|
||||
)
|
||||
|
||||
import io "io"
|
||||
io "io"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
syntax = "proto2";
|
||||
package etcdserverpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.sizer_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
option (gogoproto.goproto_getters_all) = false;
|
||||
|
||||
message Request {
|
||||
optional uint64 ID = 1 [(gogoproto.nullable) = false];
|
||||
optional string Method = 2 [(gogoproto.nullable) = false];
|
||||
optional string Path = 3 [(gogoproto.nullable) = false];
|
||||
optional string Val = 4 [(gogoproto.nullable) = false];
|
||||
optional bool Dir = 5 [(gogoproto.nullable) = false];
|
||||
optional string PrevValue = 6 [(gogoproto.nullable) = false];
|
||||
optional uint64 PrevIndex = 7 [(gogoproto.nullable) = false];
|
||||
optional bool PrevExist = 8 [(gogoproto.nullable) = true];
|
||||
optional int64 Expiration = 9 [(gogoproto.nullable) = false];
|
||||
optional bool Wait = 10 [(gogoproto.nullable) = false];
|
||||
optional uint64 Since = 11 [(gogoproto.nullable) = false];
|
||||
optional bool Recursive = 12 [(gogoproto.nullable) = false];
|
||||
optional bool Sorted = 13 [(gogoproto.nullable) = false];
|
||||
optional bool Quorum = 14 [(gogoproto.nullable) = false];
|
||||
optional int64 Time = 15 [(gogoproto.nullable) = false];
|
||||
optional bool Stream = 16 [(gogoproto.nullable) = false];
|
||||
optional bool Refresh = 17 [(gogoproto.nullable) = true];
|
||||
}
|
||||
|
||||
message Metadata {
|
||||
optional uint64 NodeID = 1 [(gogoproto.nullable) = false];
|
||||
optional uint64 ClusterID = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
+85
-55
@@ -10,9 +10,9 @@ import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
|
||||
math "math"
|
||||
)
|
||||
|
||||
import io "io"
|
||||
io "io"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -23,6 +23,8 @@ type RequestHeader struct {
|
||||
ID uint64 `protobuf:"varint,1,opt,name=ID,json=iD,proto3" json:"ID,omitempty"`
|
||||
// username is a username that is associated with an auth token of gRPC connection
|
||||
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
|
||||
// auth_revision is a revision number of auth.authStore. It is not related to mvcc
|
||||
AuthRevision uint64 `protobuf:"varint,3,opt,name=auth_revision,json=authRevision,proto3" json:"auth_revision,omitempty"`
|
||||
}
|
||||
|
||||
func (m *RequestHeader) Reset() { *m = RequestHeader{} }
|
||||
@@ -124,6 +126,11 @@ func (m *RequestHeader) MarshalTo(data []byte) (int, error) {
|
||||
i = encodeVarintRaftInternal(data, i, uint64(len(m.Username)))
|
||||
i += copy(data[i:], m.Username)
|
||||
}
|
||||
if m.AuthRevision != 0 {
|
||||
data[i] = 0x18
|
||||
i++
|
||||
i = encodeVarintRaftInternal(data, i, uint64(m.AuthRevision))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -535,6 +542,9 @@ func (m *RequestHeader) Size() (n int) {
|
||||
if l > 0 {
|
||||
n += 1 + l + sovRaftInternal(uint64(l))
|
||||
}
|
||||
if m.AuthRevision != 0 {
|
||||
n += 1 + sovRaftInternal(uint64(m.AuthRevision))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -765,6 +775,25 @@ func (m *RequestHeader) Unmarshal(data []byte) error {
|
||||
}
|
||||
m.Username = string(data[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field AuthRevision", wireType)
|
||||
}
|
||||
m.AuthRevision = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowRaftInternal
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
m.AuthRevision |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipRaftInternal(data[iNdEx:])
|
||||
@@ -2006,57 +2035,58 @@ var (
|
||||
)
|
||||
|
||||
var fileDescriptorRaftInternal = []byte{
|
||||
// 824 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x96, 0xdd, 0x4e, 0xdb, 0x48,
|
||||
0x14, 0xc7, 0x49, 0xf8, 0xcc, 0x24, 0x64, 0xd9, 0x01, 0x76, 0x67, 0x83, 0x94, 0x85, 0xac, 0x76,
|
||||
0x97, 0x7e, 0xd1, 0x0a, 0x2e, 0x7b, 0xd1, 0xa6, 0x04, 0x01, 0x12, 0x42, 0xc8, 0xa2, 0x52, 0xa5,
|
||||
0x5e, 0x58, 0x43, 0x7c, 0x48, 0x5c, 0x1c, 0xdb, 0xb5, 0x27, 0x29, 0x7d, 0x93, 0x3e, 0x46, 0xbf,
|
||||
0x1e, 0x82, 0x8b, 0x7e, 0xd0, 0xf6, 0x05, 0x5a, 0x7a, 0xd3, 0xab, 0xde, 0xb4, 0x0f, 0x50, 0xcd,
|
||||
0x87, 0xc7, 0x71, 0x32, 0xe1, 0xce, 0x3e, 0xe7, 0x7f, 0x7e, 0xe7, 0x4c, 0xe6, 0x3f, 0x9e, 0xa0,
|
||||
0xf9, 0x88, 0x1e, 0x33, 0xdb, 0xf5, 0x19, 0x44, 0x3e, 0xf5, 0xd6, 0xc2, 0x28, 0x60, 0x01, 0x2e,
|
||||
0x01, 0x6b, 0x3a, 0x31, 0x44, 0x3d, 0x88, 0xc2, 0xa3, 0xca, 0x42, 0x2b, 0x68, 0x05, 0x22, 0x71,
|
||||
0x93, 0x3f, 0x49, 0x4d, 0x65, 0x2e, 0xd5, 0xa8, 0x48, 0x21, 0x0a, 0x9b, 0xf2, 0xb1, 0x76, 0x1b,
|
||||
0xcd, 0x5a, 0xf0, 0xb8, 0x0b, 0x31, 0xdb, 0x01, 0xea, 0x40, 0x84, 0xcb, 0x28, 0xbf, 0xdb, 0x20,
|
||||
0xb9, 0xe5, 0xdc, 0xea, 0x84, 0x95, 0x77, 0x1b, 0xb8, 0x82, 0x66, 0xba, 0x31, 0x6f, 0xd9, 0x01,
|
||||
0x92, 0x5f, 0xce, 0xad, 0x16, 0x2c, 0xfd, 0x5e, 0xfb, 0x5e, 0x46, 0xf3, 0xbb, 0x6a, 0x20, 0x8b,
|
||||
0x1e, 0x33, 0x45, 0x1a, 0x62, 0xfc, 0x8b, 0xf2, 0xbd, 0x75, 0x51, 0x5d, 0x5c, 0x5f, 0x5c, 0xeb,
|
||||
0x1f, 0x79, 0x4d, 0x95, 0x58, 0xf9, 0xde, 0x3a, 0xbe, 0x85, 0x26, 0x23, 0xea, 0xb7, 0x80, 0x8c,
|
||||
0x0b, 0x65, 0x65, 0x40, 0xc9, 0x53, 0x89, 0x5c, 0x0a, 0xf1, 0x55, 0x34, 0x1e, 0x76, 0x19, 0x99,
|
||||
0x10, 0x7a, 0x92, 0xd5, 0x1f, 0x74, 0x93, 0x79, 0x2c, 0x2e, 0xc2, 0x9b, 0xa8, 0xe4, 0x80, 0x07,
|
||||
0x0c, 0x6c, 0xd9, 0x64, 0x52, 0x14, 0x2d, 0x67, 0x8b, 0x1a, 0x42, 0x91, 0x69, 0x55, 0x74, 0xd2,
|
||||
0x18, 0x6f, 0xc8, 0x4e, 0x7d, 0x32, 0x65, 0x6a, 0x78, 0x78, 0xea, 0xeb, 0x86, 0xec, 0xd4, 0xc7,
|
||||
0x77, 0x10, 0x6a, 0x06, 0x9d, 0x90, 0x36, 0x99, 0x1b, 0xf8, 0x64, 0x5a, 0x94, 0xfc, 0x9d, 0x2d,
|
||||
0xd9, 0xd4, 0xf9, 0xa4, 0xb2, 0xaf, 0x04, 0xdf, 0x45, 0x45, 0x0f, 0x68, 0x0c, 0x76, 0x2b, 0xa2,
|
||||
0x3e, 0x23, 0x33, 0x26, 0xc2, 0x1e, 0x17, 0x6c, 0xf3, 0xbc, 0x26, 0x78, 0x3a, 0xc4, 0xd7, 0x2c,
|
||||
0x09, 0x11, 0xf4, 0x82, 0x13, 0x20, 0x05, 0xd3, 0x9a, 0x05, 0xc2, 0x12, 0x02, 0xbd, 0x66, 0x2f,
|
||||
0x8d, 0xf1, 0x6d, 0xa1, 0x1e, 0x8d, 0x3a, 0x04, 0x99, 0xb6, 0xa5, 0xce, 0x53, 0x7a, 0x5b, 0x84,
|
||||
0x10, 0x6f, 0xa0, 0xa9, 0xb6, 0x70, 0x13, 0x71, 0x44, 0xc9, 0x92, 0x71, 0xcf, 0xa5, 0xe1, 0x2c,
|
||||
0x25, 0xc5, 0x75, 0x54, 0xa4, 0x5d, 0xd6, 0xb6, 0xc1, 0xa7, 0x47, 0x1e, 0x90, 0x6f, 0xc6, 0x1f,
|
||||
0xac, 0xde, 0x65, 0xed, 0x2d, 0x21, 0xd0, 0xcb, 0xa5, 0x3a, 0x84, 0x1b, 0xa8, 0x24, 0x10, 0x8e,
|
||||
0x1b, 0x0b, 0xc6, 0x8f, 0x69, 0xd3, 0x7a, 0x39, 0xa3, 0x21, 0x15, 0x7a, 0xbd, 0x34, 0x8d, 0xe1,
|
||||
0x7d, 0x49, 0x01, 0x9f, 0xb9, 0x4d, 0xca, 0x80, 0xfc, 0x94, 0x94, 0x2b, 0x59, 0x4a, 0xe2, 0xfb,
|
||||
0x7a, 0x9f, 0x34, 0xc1, 0x65, 0xea, 0xf1, 0x16, 0x9a, 0x15, 0x53, 0xf1, 0x63, 0x63, 0x53, 0xc7,
|
||||
0x21, 0x6f, 0x66, 0x46, 0x8d, 0x75, 0x3f, 0x86, 0xa8, 0xee, 0x38, 0x99, 0xb1, 0x54, 0x0c, 0xef,
|
||||
0xa3, 0xb9, 0x14, 0x23, 0x3d, 0x49, 0xde, 0x4a, 0xd2, 0x3f, 0x66, 0x92, 0x32, 0xb3, 0x82, 0x95,
|
||||
0x69, 0x26, 0x9c, 0x1d, 0xab, 0x05, 0x8c, 0xbc, 0xbb, 0x74, 0xac, 0x6d, 0x60, 0x43, 0x63, 0x6d,
|
||||
0x03, 0xc3, 0x2d, 0xf4, 0x57, 0x8a, 0x69, 0xb6, 0xf9, 0x29, 0xb1, 0x43, 0x1a, 0xc7, 0x4f, 0x82,
|
||||
0xc8, 0x21, 0xef, 0x25, 0xf2, 0x9a, 0x19, 0xb9, 0x29, 0xd4, 0x07, 0x4a, 0x9c, 0xd0, 0xff, 0xa0,
|
||||
0xc6, 0x34, 0x7e, 0x80, 0x16, 0xfa, 0xe6, 0xe5, 0xf6, 0xb6, 0xa3, 0xc0, 0x03, 0x72, 0x2e, 0x7b,
|
||||
0xfc, 0x37, 0x62, 0x6c, 0x71, 0x34, 0x82, 0x74, 0xab, 0x7f, 0xa7, 0x83, 0x19, 0xfc, 0x10, 0x2d,
|
||||
0xa6, 0x64, 0x79, 0x52, 0x24, 0xfa, 0x83, 0x44, 0xff, 0x6f, 0x46, 0xab, 0x23, 0xd3, 0xc7, 0xc6,
|
||||
0x74, 0x28, 0x85, 0x77, 0x50, 0x39, 0x85, 0x7b, 0x6e, 0xcc, 0xc8, 0x47, 0x49, 0x5d, 0x31, 0x53,
|
||||
0xf7, 0xdc, 0x98, 0x65, 0x7c, 0x94, 0x04, 0x35, 0x89, 0x8f, 0x26, 0x49, 0x9f, 0x46, 0x92, 0x78,
|
||||
0xeb, 0x21, 0x52, 0x12, 0xd4, 0x5b, 0x2f, 0x48, 0xdc, 0x91, 0xcf, 0x0b, 0xa3, 0xb6, 0x9e, 0xd7,
|
||||
0x0c, 0x3a, 0x52, 0xc5, 0xb4, 0x23, 0x05, 0x46, 0x39, 0xf2, 0x45, 0x61, 0x94, 0x23, 0x79, 0x95,
|
||||
0xc1, 0x91, 0x69, 0x38, 0x3b, 0x16, 0x77, 0xe4, 0xcb, 0x4b, 0xc7, 0x1a, 0x74, 0xa4, 0x8a, 0xe1,
|
||||
0x47, 0xa8, 0xd2, 0x87, 0x11, 0x46, 0x09, 0x21, 0xea, 0xb8, 0x71, 0xcc, 0xbf, 0xc3, 0xaf, 0x24,
|
||||
0xf3, 0xfa, 0x08, 0x26, 0x97, 0x1f, 0x68, 0x75, 0xc2, 0xff, 0x93, 0x9a, 0xf3, 0xb8, 0x83, 0x96,
|
||||
0xd2, 0x5e, 0xca, 0x3a, 0x7d, 0xcd, 0x5e, 0xcb, 0x66, 0x37, 0xcc, 0xcd, 0xa4, 0x4b, 0x86, 0xbb,
|
||||
0x11, 0x3a, 0x42, 0x50, 0xfb, 0x0d, 0xcd, 0x6e, 0x75, 0x42, 0xf6, 0xd4, 0x82, 0x38, 0x0c, 0xfc,
|
||||
0x18, 0x6a, 0x21, 0x5a, 0xba, 0xe4, 0x43, 0x84, 0x31, 0x9a, 0x10, 0x17, 0x77, 0x4e, 0x5c, 0xdc,
|
||||
0xe2, 0x99, 0x5f, 0xe8, 0xfa, 0x7c, 0xaa, 0x0b, 0x3d, 0x79, 0xc7, 0x2b, 0xa8, 0x14, 0xbb, 0x9d,
|
||||
0xd0, 0x03, 0x9b, 0x05, 0x27, 0xe0, 0x8b, 0x8b, 0xb8, 0x60, 0x15, 0x65, 0xec, 0x90, 0x87, 0xee,
|
||||
0x2d, 0x9c, 0x7d, 0xa9, 0x8e, 0x9d, 0x5d, 0x54, 0x73, 0xe7, 0x17, 0xd5, 0xdc, 0xe7, 0x8b, 0x6a,
|
||||
0xee, 0xd9, 0xd7, 0xea, 0xd8, 0xd1, 0x94, 0xf8, 0x37, 0xb1, 0xf1, 0x2b, 0x00, 0x00, 0xff, 0xff,
|
||||
0x54, 0x8c, 0x4a, 0x7f, 0xa5, 0x08, 0x00, 0x00,
|
||||
// 837 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x96, 0xdd, 0x52, 0xd3, 0x40,
|
||||
0x14, 0xc7, 0x69, 0xf9, 0xec, 0xb6, 0x54, 0x5c, 0x40, 0xd7, 0x32, 0x53, 0xa1, 0x8c, 0x8a, 0x5f,
|
||||
0xe8, 0x94, 0x07, 0xd0, 0x4a, 0x19, 0x60, 0x86, 0x61, 0x98, 0x0c, 0xce, 0x38, 0xe3, 0x45, 0x5c,
|
||||
0x9a, 0x43, 0x1b, 0x49, 0x93, 0xb8, 0xd9, 0x56, 0x7c, 0x13, 0x1f, 0xc3, 0xaf, 0x87, 0xe0, 0xc2,
|
||||
0x0f, 0xd4, 0x17, 0x50, 0xbc, 0xf1, 0xca, 0x1b, 0x7d, 0x00, 0x67, 0x3f, 0x92, 0x34, 0x6d, 0xca,
|
||||
0x5d, 0x72, 0xce, 0xff, 0xfc, 0xce, 0xd9, 0xec, 0x7f, 0xbb, 0x45, 0xb3, 0x8c, 0x1e, 0x72, 0xd3,
|
||||
0x76, 0x39, 0x30, 0x97, 0x3a, 0xab, 0x3e, 0xf3, 0xb8, 0x87, 0x0b, 0xc0, 0x1b, 0x56, 0x00, 0xac,
|
||||
0x0b, 0xcc, 0x3f, 0x28, 0xcd, 0x35, 0xbd, 0xa6, 0x27, 0x13, 0xf7, 0xc4, 0x93, 0xd2, 0x94, 0x66,
|
||||
0x62, 0x8d, 0x8e, 0xe4, 0x98, 0xdf, 0x50, 0x8f, 0x95, 0x67, 0x68, 0xda, 0x80, 0x17, 0x1d, 0x08,
|
||||
0xf8, 0x16, 0x50, 0x0b, 0x18, 0x2e, 0xa2, 0xec, 0x76, 0x9d, 0x64, 0x16, 0x33, 0x2b, 0x63, 0x46,
|
||||
0xd6, 0xae, 0xe3, 0x12, 0x9a, 0xea, 0x04, 0xa2, 0x65, 0x1b, 0x48, 0x76, 0x31, 0xb3, 0x92, 0x33,
|
||||
0xa2, 0x77, 0xbc, 0x8c, 0xa6, 0x69, 0x87, 0xb7, 0x4c, 0x06, 0x5d, 0x3b, 0xb0, 0x3d, 0x97, 0x8c,
|
||||
0xca, 0xb2, 0x82, 0x08, 0x1a, 0x3a, 0x56, 0xf9, 0x53, 0x44, 0xb3, 0xdb, 0x7a, 0x6a, 0x83, 0x1e,
|
||||
0x72, 0xdd, 0x6e, 0xa0, 0xd1, 0x35, 0x94, 0xed, 0x56, 0x65, 0x8b, 0x7c, 0x75, 0x7e, 0xb5, 0x77,
|
||||
0x5d, 0xab, 0xba, 0xc4, 0xc8, 0x76, 0xab, 0xf8, 0x3e, 0x1a, 0x67, 0xd4, 0x6d, 0x82, 0xec, 0x95,
|
||||
0xaf, 0x96, 0xfa, 0x94, 0x22, 0x15, 0xca, 0x95, 0x10, 0xdf, 0x42, 0xa3, 0x7e, 0x87, 0x93, 0x31,
|
||||
0xa9, 0x27, 0x49, 0xfd, 0x5e, 0x27, 0x9c, 0xc7, 0x10, 0x22, 0xbc, 0x8e, 0x0a, 0x16, 0x38, 0xc0,
|
||||
0xc1, 0x54, 0x4d, 0xc6, 0x65, 0xd1, 0x62, 0xb2, 0xa8, 0x2e, 0x15, 0x89, 0x56, 0x79, 0x2b, 0x8e,
|
||||
0x89, 0x86, 0xfc, 0xd8, 0x25, 0x13, 0x69, 0x0d, 0xf7, 0x8f, 0xdd, 0xa8, 0x21, 0x3f, 0x76, 0xf1,
|
||||
0x03, 0x84, 0x1a, 0x5e, 0xdb, 0xa7, 0x0d, 0x2e, 0xbe, 0xdf, 0xa4, 0x2c, 0xb9, 0x9a, 0x2c, 0x59,
|
||||
0x8f, 0xf2, 0x61, 0x65, 0x4f, 0x09, 0x7e, 0x88, 0xf2, 0x0e, 0xd0, 0x00, 0xcc, 0x26, 0xa3, 0x2e,
|
||||
0x27, 0x53, 0x69, 0x84, 0x1d, 0x21, 0xd8, 0x14, 0xf9, 0x88, 0xe0, 0x44, 0x21, 0xb1, 0x66, 0x45,
|
||||
0x60, 0xd0, 0xf5, 0x8e, 0x80, 0xe4, 0xd2, 0xd6, 0x2c, 0x11, 0x86, 0x14, 0x44, 0x6b, 0x76, 0xe2,
|
||||
0x98, 0xd8, 0x16, 0xea, 0x50, 0xd6, 0x26, 0x28, 0x6d, 0x5b, 0x6a, 0x22, 0x15, 0x6d, 0x8b, 0x14,
|
||||
0xe2, 0x35, 0x34, 0xd1, 0x92, 0x96, 0x23, 0x96, 0x2c, 0x59, 0x48, 0xdd, 0x73, 0xe5, 0x4a, 0x43,
|
||||
0x4b, 0x71, 0x0d, 0xe5, 0xa5, 0xe3, 0xc0, 0xa5, 0x07, 0x0e, 0x90, 0xdf, 0xa9, 0x1f, 0xac, 0xd6,
|
||||
0xe1, 0xad, 0x0d, 0x29, 0x88, 0x96, 0x4b, 0xa3, 0x10, 0xae, 0x23, 0xe9, 0x4f, 0xd3, 0xb2, 0x03,
|
||||
0xc9, 0xf8, 0x3b, 0x99, 0xb6, 0x5e, 0xc1, 0xa8, 0x2b, 0x45, 0xb4, 0x5e, 0x1a, 0xc7, 0xf0, 0xae,
|
||||
0xa2, 0x80, 0xcb, 0xed, 0x06, 0xe5, 0x40, 0xfe, 0x29, 0xca, 0xcd, 0x24, 0x25, 0xf4, 0x7d, 0xad,
|
||||
0x47, 0x1a, 0xe2, 0x12, 0xf5, 0x78, 0x43, 0x1f, 0x25, 0x71, 0xb6, 0x4c, 0x6a, 0x59, 0xe4, 0xe3,
|
||||
0xd4, 0xb0, 0xb1, 0x1e, 0x07, 0xc0, 0x6a, 0x96, 0x95, 0x18, 0x4b, 0xc7, 0xf0, 0x2e, 0x9a, 0x89,
|
||||
0x31, 0xca, 0x93, 0xe4, 0x93, 0x22, 0x2d, 0xa7, 0x93, 0xb4, 0x99, 0x35, 0xac, 0x48, 0x13, 0xe1,
|
||||
0xe4, 0x58, 0x4d, 0xe0, 0xe4, 0xf3, 0xb9, 0x63, 0x6d, 0x02, 0x1f, 0x18, 0x6b, 0x13, 0x38, 0x6e,
|
||||
0xa2, 0x2b, 0x31, 0xa6, 0xd1, 0x12, 0xa7, 0xc4, 0xf4, 0x69, 0x10, 0xbc, 0xf4, 0x98, 0x45, 0xbe,
|
||||
0x28, 0xe4, 0xed, 0x74, 0xe4, 0xba, 0x54, 0xef, 0x69, 0x71, 0x48, 0xbf, 0x44, 0x53, 0xd3, 0xf8,
|
||||
0x09, 0x9a, 0xeb, 0x99, 0x57, 0xd8, 0xdb, 0x64, 0x9e, 0x03, 0xe4, 0x54, 0xf5, 0xb8, 0x3e, 0x64,
|
||||
0x6c, 0x79, 0x34, 0xbc, 0x78, 0xab, 0x2f, 0xd2, 0xfe, 0x0c, 0x7e, 0x8a, 0xe6, 0x63, 0xb2, 0x3a,
|
||||
0x29, 0x0a, 0xfd, 0x55, 0xa1, 0x6f, 0xa4, 0xa3, 0xf5, 0x91, 0xe9, 0x61, 0x63, 0x3a, 0x90, 0xc2,
|
||||
0x5b, 0xa8, 0x18, 0xc3, 0x1d, 0x3b, 0xe0, 0xe4, 0x9b, 0xa2, 0x2e, 0xa5, 0x53, 0x77, 0xec, 0x80,
|
||||
0x27, 0x7c, 0x14, 0x06, 0x23, 0x92, 0x18, 0x4d, 0x91, 0xbe, 0x0f, 0x25, 0x89, 0xd6, 0x03, 0xa4,
|
||||
0x30, 0x18, 0x6d, 0xbd, 0x24, 0x09, 0x47, 0xbe, 0xc9, 0x0d, 0xdb, 0x7a, 0x51, 0xd3, 0xef, 0x48,
|
||||
0x1d, 0x8b, 0x1c, 0x29, 0x31, 0xda, 0x91, 0x6f, 0x73, 0xc3, 0x1c, 0x29, 0xaa, 0x52, 0x1c, 0x19,
|
||||
0x87, 0x93, 0x63, 0x09, 0x47, 0xbe, 0x3b, 0x77, 0xac, 0x7e, 0x47, 0xea, 0x18, 0x7e, 0x8e, 0x4a,
|
||||
0x3d, 0x18, 0x69, 0x14, 0x1f, 0x58, 0xdb, 0x0e, 0xe4, 0x3d, 0xf6, 0x5e, 0x31, 0xef, 0x0c, 0x61,
|
||||
0x0a, 0xf9, 0x5e, 0xa4, 0x0e, 0xf9, 0x97, 0x69, 0x7a, 0x1e, 0xb7, 0xd1, 0x42, 0xdc, 0x4b, 0x5b,
|
||||
0xa7, 0xa7, 0xd9, 0x07, 0xd5, 0xec, 0x6e, 0x7a, 0x33, 0xe5, 0x92, 0xc1, 0x6e, 0x84, 0x0e, 0x11,
|
||||
0x54, 0x2e, 0xa0, 0xe9, 0x8d, 0xb6, 0xcf, 0x5f, 0x19, 0x10, 0xf8, 0x9e, 0x1b, 0x40, 0xc5, 0x47,
|
||||
0x0b, 0xe7, 0xfc, 0x10, 0x61, 0x8c, 0xc6, 0xe4, 0xed, 0x9e, 0x91, 0xb7, 0xbb, 0x7c, 0x16, 0xb7,
|
||||
0x7e, 0x74, 0x3e, 0xf5, 0xad, 0x1f, 0xbe, 0xe3, 0x25, 0x54, 0x08, 0xec, 0xb6, 0xef, 0x80, 0xc9,
|
||||
0xbd, 0x23, 0x50, 0x97, 0x7e, 0xce, 0xc8, 0xab, 0xd8, 0xbe, 0x08, 0x3d, 0x9a, 0x3b, 0xf9, 0x59,
|
||||
0x1e, 0x39, 0x39, 0x2b, 0x67, 0x4e, 0xcf, 0xca, 0x99, 0x1f, 0x67, 0xe5, 0xcc, 0xeb, 0x5f, 0xe5,
|
||||
0x91, 0x83, 0x09, 0xf9, 0x97, 0x63, 0xed, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x23, 0xd2,
|
||||
0x00, 0xca, 0x08, 0x00, 0x00,
|
||||
}
|
||||
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package etcdserverpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "etcdserver.proto";
|
||||
import "rpc.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.sizer_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
option (gogoproto.goproto_getters_all) = false;
|
||||
|
||||
message RequestHeader {
|
||||
uint64 ID = 1;
|
||||
// username is a username that is associated with an auth token of gRPC connection
|
||||
string username = 2;
|
||||
}
|
||||
|
||||
// An InternalRaftRequest is the union of all requests which can be
|
||||
// sent via raft.
|
||||
message InternalRaftRequest {
|
||||
RequestHeader header = 100;
|
||||
uint64 ID = 1;
|
||||
|
||||
Request v2 = 2;
|
||||
|
||||
RangeRequest range = 3;
|
||||
PutRequest put = 4;
|
||||
DeleteRangeRequest delete_range = 5;
|
||||
TxnRequest txn = 6;
|
||||
CompactionRequest compaction = 7;
|
||||
|
||||
LeaseGrantRequest lease_grant = 8;
|
||||
LeaseRevokeRequest lease_revoke = 9;
|
||||
|
||||
AlarmRequest alarm = 10;
|
||||
|
||||
AuthEnableRequest auth_enable = 1000;
|
||||
AuthDisableRequest auth_disable = 1011;
|
||||
|
||||
InternalAuthenticateRequest authenticate = 1012;
|
||||
|
||||
AuthUserAddRequest auth_user_add = 1100;
|
||||
AuthUserDeleteRequest auth_user_delete = 1101;
|
||||
AuthUserGetRequest auth_user_get = 1102;
|
||||
AuthUserChangePasswordRequest auth_user_change_password = 1103;
|
||||
AuthUserGrantRoleRequest auth_user_grant_role = 1104;
|
||||
AuthUserRevokeRoleRequest auth_user_revoke_role = 1105;
|
||||
AuthUserListRequest auth_user_list = 1106;
|
||||
AuthRoleListRequest auth_role_list = 1107;
|
||||
|
||||
AuthRoleAddRequest auth_role_add = 1200;
|
||||
AuthRoleDeleteRequest auth_role_delete = 1201;
|
||||
AuthRoleGetRequest auth_role_get = 1202;
|
||||
AuthRoleGrantPermissionRequest auth_role_grant_permission = 1203;
|
||||
AuthRoleRevokePermissionRequest auth_role_revoke_permission = 1204;
|
||||
}
|
||||
|
||||
message EmptyResponse {
|
||||
}
|
||||
|
||||
// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
|
||||
// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
|
||||
// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
|
||||
message InternalAuthenticateRequest {
|
||||
string name = 1;
|
||||
string password = 2;
|
||||
|
||||
// simple_token is generated in API layer (etcdserver/v3_server.go)
|
||||
string simple_token = 3;
|
||||
}
|
||||
|
||||
+1215
-346
File diff suppressed because it is too large
Load Diff
+45
@@ -222,6 +222,19 @@ func request_Lease_LeaseKeepAlive_0(ctx context.Context, marshaler runtime.Marsh
|
||||
return stream, metadata, nil
|
||||
}
|
||||
|
||||
func request_Lease_LeaseTimeToLive_0(ctx context.Context, marshaler runtime.Marshaler, client LeaseClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq LeaseTimeToLiveRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
|
||||
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.LeaseTimeToLive(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Cluster_MemberAdd_0(ctx context.Context, marshaler runtime.Marshaler, client ClusterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq MemberAddRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -935,6 +948,34 @@ func RegisterLeaseHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Lease_LeaseTimeToLive_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
if cn, ok := w.(http.CloseNotifier); ok {
|
||||
go func(done <-chan struct{}, closed <-chan bool) {
|
||||
select {
|
||||
case <-done:
|
||||
case <-closed:
|
||||
cancel()
|
||||
}
|
||||
}(ctx.Done(), cn.CloseNotify())
|
||||
}
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
|
||||
}
|
||||
resp, md, err := request_Lease_LeaseTimeToLive_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Lease_LeaseTimeToLive_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -944,6 +985,8 @@ var (
|
||||
pattern_Lease_LeaseRevoke_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3alpha", "kv", "lease", "revoke"}, ""))
|
||||
|
||||
pattern_Lease_LeaseKeepAlive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v3alpha", "lease", "keepalive"}, ""))
|
||||
|
||||
pattern_Lease_LeaseTimeToLive_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v3alpha", "kv", "lease", "timetolive"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -952,6 +995,8 @@ var (
|
||||
forward_Lease_LeaseRevoke_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Lease_LeaseKeepAlive_0 = runtime.ForwardResponseStream
|
||||
|
||||
forward_Lease_LeaseTimeToLive_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
||||
// RegisterClusterHandlerFromEndpoint is same as RegisterClusterHandler but
|
||||
|
||||
-894
@@ -1,894 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package etcdserverpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "etcd/mvcc/mvccpb/kv.proto";
|
||||
import "etcd/auth/authpb/auth.proto";
|
||||
|
||||
// for grpc-gateway
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
|
||||
service KV {
|
||||
// Range gets the keys in the range from the key-value store.
|
||||
rpc Range(RangeRequest) returns (RangeResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/range"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Put puts the given key into the key-value store.
|
||||
// A put request increments the revision of the key-value store
|
||||
// and generates one event in the event history.
|
||||
rpc Put(PutRequest) returns (PutResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/put"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// DeleteRange deletes the given range from the key-value store.
|
||||
// A delete request increments the revision of the key-value store
|
||||
// and generates a delete event in the event history for every deleted key.
|
||||
rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/deleterange"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Txn processes multiple requests in a single transaction.
|
||||
// A txn request increments the revision of the key-value store
|
||||
// and generates events with the same revision for every completed request.
|
||||
// It is not allowed to modify the same key several times within one txn.
|
||||
rpc Txn(TxnRequest) returns (TxnResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/txn"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Compact compacts the event history in the etcd key-value store. The key-value
|
||||
// store should be periodically compacted or the event history will continue to grow
|
||||
// indefinitely.
|
||||
rpc Compact(CompactionRequest) returns (CompactionResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/compaction"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service Watch {
|
||||
// Watch watches for events happening or that have happened. Both input and output
|
||||
// are streams; the input stream is for creating and canceling watchers and the output
|
||||
// stream sends events. One watch RPC can watch on multiple key ranges, streaming events
|
||||
// for several watches at once. The entire event history can be watched starting from the
|
||||
// last compaction revision.
|
||||
rpc Watch(stream WatchRequest) returns (stream WatchResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/watch"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service Lease {
|
||||
// LeaseGrant creates a lease which expires if the server does not receive a keepAlive
|
||||
// within a given time to live period. All keys attached to the lease will be expired and
|
||||
// deleted if the lease expires. Each expired key generates a delete event in the event history.
|
||||
rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/lease/grant"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
|
||||
rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/kv/lease/revoke"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
|
||||
// to the server and streaming keep alive responses from the server to the client.
|
||||
rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/lease/keepalive"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(xiangli) List all existing Leases?
|
||||
// TODO(xiangli) Get details information (expirations, leased keys, etc.) of a lease?
|
||||
}
|
||||
|
||||
service Cluster {
|
||||
// MemberAdd adds a member into the cluster.
|
||||
rpc MemberAdd(MemberAddRequest) returns (MemberAddResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/cluster/member/add"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// MemberRemove removes an existing member from the cluster.
|
||||
rpc MemberRemove(MemberRemoveRequest) returns (MemberRemoveResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/cluster/member/remove"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// MemberUpdate updates the member configuration.
|
||||
rpc MemberUpdate(MemberUpdateRequest) returns (MemberUpdateResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/cluster/member/update"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// MemberList lists all the members in the cluster.
|
||||
rpc MemberList(MemberListRequest) returns (MemberListResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/cluster/member/list"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service Maintenance {
|
||||
// Alarm activates, deactivates, and queries alarms regarding cluster health.
|
||||
rpc Alarm(AlarmRequest) returns (AlarmResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/maintenance/alarm"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Status gets the status of the member.
|
||||
rpc Status(StatusRequest) returns (StatusResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/maintenance/status"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Defragment defragments a member's backend database to recover storage space.
|
||||
rpc Defragment(DefragmentRequest) returns (DefragmentResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/maintenance/defragment"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Hash returns the hash of the local KV state for consistency checking purpose.
|
||||
// This is designed for testing; do not use this in production when there
|
||||
// are ongoing transactions.
|
||||
rpc Hash(HashRequest) returns (HashResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/maintenance/hash"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
|
||||
rpc Snapshot(SnapshotRequest) returns (stream SnapshotResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/maintenance/snapshot"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
service Auth {
|
||||
// AuthEnable enables authentication.
|
||||
rpc AuthEnable(AuthEnableRequest) returns (AuthEnableResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/enable"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// AuthDisable disables authentication.
|
||||
rpc AuthDisable(AuthDisableRequest) returns (AuthDisableResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/disable"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// Authenticate processes an authenticate request.
|
||||
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/authenticate"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserAdd adds a new user.
|
||||
rpc UserAdd(AuthUserAddRequest) returns (AuthUserAddResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/add"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserGet gets detailed user information.
|
||||
rpc UserGet(AuthUserGetRequest) returns (AuthUserGetResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/get"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserList gets a list of all users.
|
||||
rpc UserList(AuthUserListRequest) returns (AuthUserListResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/list"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserDelete deletes a specified user.
|
||||
rpc UserDelete(AuthUserDeleteRequest) returns (AuthUserDeleteResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/delete"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserChangePassword changes the password of a specified user.
|
||||
rpc UserChangePassword(AuthUserChangePasswordRequest) returns (AuthUserChangePasswordResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/changepw"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserGrant grants a role to a specified user.
|
||||
rpc UserGrantRole(AuthUserGrantRoleRequest) returns (AuthUserGrantRoleResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/grant"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// UserRevokeRole revokes a role of specified user.
|
||||
rpc UserRevokeRole(AuthUserRevokeRoleRequest) returns (AuthUserRevokeRoleResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/user/revoke"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleAdd adds a new role.
|
||||
rpc RoleAdd(AuthRoleAddRequest) returns (AuthRoleAddResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/add"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleGet gets detailed role information.
|
||||
rpc RoleGet(AuthRoleGetRequest) returns (AuthRoleGetResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/get"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleList gets lists of all roles.
|
||||
rpc RoleList(AuthRoleListRequest) returns (AuthRoleListResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/list"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleDelete deletes a specified role.
|
||||
rpc RoleDelete(AuthRoleDeleteRequest) returns (AuthRoleDeleteResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/delete"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleGrantPermission grants a permission of a specified key or range to a specified role.
|
||||
rpc RoleGrantPermission(AuthRoleGrantPermissionRequest) returns (AuthRoleGrantPermissionResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/grant"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
// RoleRevokePermission revokes a key or range permission of a specified role.
|
||||
rpc RoleRevokePermission(AuthRoleRevokePermissionRequest) returns (AuthRoleRevokePermissionResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3alpha/auth/role/revoke"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message ResponseHeader {
|
||||
// cluster_id is the ID of the cluster which sent the response.
|
||||
uint64 cluster_id = 1;
|
||||
// member_id is the ID of the member which sent the response.
|
||||
uint64 member_id = 2;
|
||||
// revision is the key-value store revision when the request was applied.
|
||||
int64 revision = 3;
|
||||
// raft_term is the raft term when the request was applied.
|
||||
uint64 raft_term = 4;
|
||||
}
|
||||
|
||||
message RangeRequest {
|
||||
enum SortOrder {
|
||||
NONE = 0; // default, no sorting
|
||||
ASCEND = 1; // lowest target value first
|
||||
DESCEND = 2; // highest target value first
|
||||
}
|
||||
enum SortTarget {
|
||||
KEY = 0;
|
||||
VERSION = 1;
|
||||
CREATE = 2;
|
||||
MOD = 3;
|
||||
VALUE = 4;
|
||||
}
|
||||
|
||||
// key is the first key for the range. If range_end is not given, the request only looks up key.
|
||||
bytes key = 1;
|
||||
// range_end is the upper bound on the requested range [key, range_end).
|
||||
// If range_end is '\0', the range is all keys >= key.
|
||||
// If the range_end is one bit larger than the given key,
|
||||
// then the range requests get the all keys with the prefix (the given key).
|
||||
// If both key and range_end are '\0', then range requests returns all keys.
|
||||
bytes range_end = 2;
|
||||
// limit is a limit on the number of keys returned for the request.
|
||||
int64 limit = 3;
|
||||
// revision is the point-in-time of the key-value store to use for the range.
|
||||
// If revision is less or equal to zero, the range is over the newest key-value store.
|
||||
// If the revision has been compacted, ErrCompacted is returned as a response.
|
||||
int64 revision = 4;
|
||||
|
||||
// sort_order is the order for returned sorted results.
|
||||
SortOrder sort_order = 5;
|
||||
|
||||
// sort_target is the key-value field to use for sorting.
|
||||
SortTarget sort_target = 6;
|
||||
|
||||
// serializable sets the range request to use serializable member-local reads.
|
||||
// Range requests are linearizable by default; linearizable requests have higher
|
||||
// latency and lower throughput than serializable requests but reflect the current
|
||||
// consensus of the cluster. For better performance, in exchange for possible stale reads,
|
||||
// a serializable range request is served locally without needing to reach consensus
|
||||
// with other nodes in the cluster.
|
||||
bool serializable = 7;
|
||||
|
||||
// keys_only when set returns only the keys and not the values.
|
||||
bool keys_only = 8;
|
||||
|
||||
// count_only when set returns only the count of the keys in the range.
|
||||
bool count_only = 9;
|
||||
}
|
||||
|
||||
message RangeResponse {
|
||||
ResponseHeader header = 1;
|
||||
// kvs is the list of key-value pairs matched by the range request.
|
||||
// kvs is empty when count is requested.
|
||||
repeated mvccpb.KeyValue kvs = 2;
|
||||
// more indicates if there are more keys to return in the requested range.
|
||||
bool more = 3;
|
||||
// count is set to the number of keys within the range when requested.
|
||||
int64 count = 4;
|
||||
}
|
||||
|
||||
message PutRequest {
|
||||
// key is the key, in bytes, to put into the key-value store.
|
||||
bytes key = 1;
|
||||
// value is the value, in bytes, to associate with the key in the key-value store.
|
||||
bytes value = 2;
|
||||
// lease is the lease ID to associate with the key in the key-value store. A lease
|
||||
// value of 0 indicates no lease.
|
||||
int64 lease = 3;
|
||||
}
|
||||
|
||||
message PutResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message DeleteRangeRequest {
|
||||
// key is the first key to delete in the range.
|
||||
bytes key = 1;
|
||||
// range_end is the key following the last key to delete for the range [key, range_end).
|
||||
// If range_end is not given, the range is defined to contain only the key argument.
|
||||
// If range_end is '\0', the range is all keys greater than or equal to the key argument.
|
||||
bytes range_end = 2;
|
||||
}
|
||||
|
||||
message DeleteRangeResponse {
|
||||
ResponseHeader header = 1;
|
||||
// deleted is the number of keys deleted by the delete range request.
|
||||
int64 deleted = 2;
|
||||
}
|
||||
|
||||
message RequestOp {
|
||||
// request is a union of request types accepted by a transaction.
|
||||
oneof request {
|
||||
RangeRequest request_range = 1;
|
||||
PutRequest request_put = 2;
|
||||
DeleteRangeRequest request_delete_range = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message ResponseOp {
|
||||
// response is a union of response types returned by a transaction.
|
||||
oneof response {
|
||||
RangeResponse response_range = 1;
|
||||
PutResponse response_put = 2;
|
||||
DeleteRangeResponse response_delete_range = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message Compare {
|
||||
enum CompareResult {
|
||||
EQUAL = 0;
|
||||
GREATER = 1;
|
||||
LESS = 2;
|
||||
}
|
||||
enum CompareTarget {
|
||||
VERSION = 0;
|
||||
CREATE = 1;
|
||||
MOD = 2;
|
||||
VALUE= 3;
|
||||
}
|
||||
// result is logical comparison operation for this comparison.
|
||||
CompareResult result = 1;
|
||||
// target is the key-value field to inspect for the comparison.
|
||||
CompareTarget target = 2;
|
||||
// key is the subject key for the comparison operation.
|
||||
bytes key = 3;
|
||||
oneof target_union {
|
||||
// version is the version of the given key
|
||||
int64 version = 4;
|
||||
// create_revision is the creation revision of the given key
|
||||
int64 create_revision = 5;
|
||||
// mod_revision is the last modified revision of the given key.
|
||||
int64 mod_revision = 6;
|
||||
// value is the value of the given key, in bytes.
|
||||
bytes value = 7;
|
||||
}
|
||||
}
|
||||
|
||||
// From google paxosdb paper:
|
||||
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
|
||||
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
|
||||
// and consists of three components:
|
||||
// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
|
||||
// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
|
||||
// may apply to the same or different entries in the database. All tests in the guard are applied and
|
||||
// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
|
||||
// it executes f op (see item 3 below).
|
||||
// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
|
||||
// lookup operation, and applies to a single database entry. Two different operations in the list may apply
|
||||
// to the same or different entries in the database. These operations are executed
|
||||
// if guard evaluates to
|
||||
// true.
|
||||
// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
|
||||
message TxnRequest {
|
||||
// compare is a list of predicates representing a conjunction of terms.
|
||||
// If the comparisons succeed, then the success requests will be processed in order,
|
||||
// and the response will contain their respective responses in order.
|
||||
// If the comparisons fail, then the failure requests will be processed in order,
|
||||
// and the response will contain their respective responses in order.
|
||||
repeated Compare compare = 1;
|
||||
// success is a list of requests which will be applied when compare evaluates to true.
|
||||
repeated RequestOp success = 2;
|
||||
// failure is a list of requests which will be applied when compare evaluates to false.
|
||||
repeated RequestOp failure = 3;
|
||||
}
|
||||
|
||||
message TxnResponse {
|
||||
ResponseHeader header = 1;
|
||||
// succeeded is set to true if the compare evaluated to true or false otherwise.
|
||||
bool succeeded = 2;
|
||||
// responses is a list of responses corresponding to the results from applying
|
||||
// success if succeeded is true or failure if succeeded is false.
|
||||
repeated ResponseOp responses = 3;
|
||||
}
|
||||
|
||||
// CompactionRequest compacts the key-value store up to a given revision. All superseded keys
|
||||
// with a revision less than the compaction revision will be removed.
|
||||
message CompactionRequest {
|
||||
// revision is the key-value store revision for the compaction operation.
|
||||
int64 revision = 1;
|
||||
// physical is set so the RPC will wait until the compaction is physically
|
||||
// applied to the local database such that compacted entries are totally
|
||||
// removed from the backend database.
|
||||
bool physical = 2;
|
||||
}
|
||||
|
||||
message CompactionResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message HashRequest {
|
||||
}
|
||||
|
||||
message HashResponse {
|
||||
ResponseHeader header = 1;
|
||||
// hash is the hash value computed from the responding member's key-value store.
|
||||
uint32 hash = 2;
|
||||
}
|
||||
|
||||
message SnapshotRequest {
|
||||
}
|
||||
|
||||
message SnapshotResponse {
|
||||
// header has the current key-value store information. The first header in the snapshot
|
||||
// stream indicates the point in time of the snapshot.
|
||||
ResponseHeader header = 1;
|
||||
|
||||
// remaining_bytes is the number of blob bytes to be sent after this message
|
||||
uint64 remaining_bytes = 2;
|
||||
|
||||
// blob contains the next chunk of the snapshot in the snapshot stream.
|
||||
bytes blob = 3;
|
||||
}
|
||||
|
||||
message WatchRequest {
|
||||
// request_union is a request to either create a new watcher or cancel an existing watcher.
|
||||
oneof request_union {
|
||||
WatchCreateRequest create_request = 1;
|
||||
WatchCancelRequest cancel_request = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message WatchCreateRequest {
|
||||
// key is the key to register for watching.
|
||||
bytes key = 1;
|
||||
// range_end is the end of the range [key, range_end) to watch. If range_end is not given,
|
||||
// only the key argument is watched. If range_end is equal to '\0', all keys greater than
|
||||
// or equal to the key argument are watched.
|
||||
bytes range_end = 2;
|
||||
// start_revision is an optional revision to watch from (inclusive). No start_revision is "now".
|
||||
int64 start_revision = 3;
|
||||
// progress_notify is set so that the etcd server will periodically send a WatchResponse with
|
||||
// no events to the new watcher if there are no recent events. It is useful when clients
|
||||
// wish to recover a disconnected watcher starting from a recent known revision.
|
||||
// The etcd server may decide how often it will send notifications based on current load.
|
||||
bool progress_notify = 4;
|
||||
}
|
||||
|
||||
message WatchCancelRequest {
|
||||
// watch_id is the watcher id to cancel so that no more events are transmitted.
|
||||
int64 watch_id = 1;
|
||||
}
|
||||
|
||||
message WatchResponse {
|
||||
ResponseHeader header = 1;
|
||||
// watch_id is the ID of the watcher that corresponds to the response.
|
||||
int64 watch_id = 2;
|
||||
// created is set to true if the response is for a create watch request.
|
||||
// The client should record the watch_id and expect to receive events for
|
||||
// the created watcher from the same stream.
|
||||
// All events sent to the created watcher will attach with the same watch_id.
|
||||
bool created = 3;
|
||||
// canceled is set to true if the response is for a cancel watch request.
|
||||
// No further events will be sent to the canceled watcher.
|
||||
bool canceled = 4;
|
||||
// compact_revision is set to the minimum index if a watcher tries to watch
|
||||
// at a compacted index.
|
||||
//
|
||||
// This happens when creating a watcher at a compacted revision or the watcher cannot
|
||||
// catch up with the progress of the key-value store.
|
||||
//
|
||||
// The client should treat the watcher as canceled and should not try to create any
|
||||
// watcher with the same start_revision again.
|
||||
int64 compact_revision = 5;
|
||||
|
||||
repeated mvccpb.Event events = 11;
|
||||
}
|
||||
|
||||
message LeaseGrantRequest {
|
||||
// TTL is the advisory time-to-live in seconds.
|
||||
int64 TTL = 1;
|
||||
// ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID.
|
||||
int64 ID = 2;
|
||||
}
|
||||
|
||||
message LeaseGrantResponse {
|
||||
ResponseHeader header = 1;
|
||||
// ID is the lease ID for the granted lease.
|
||||
int64 ID = 2;
|
||||
// TTL is the server chosen lease time-to-live in seconds.
|
||||
int64 TTL = 3;
|
||||
string error = 4;
|
||||
}
|
||||
|
||||
message LeaseRevokeRequest {
|
||||
// ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted.
|
||||
int64 ID = 1;
|
||||
}
|
||||
|
||||
message LeaseRevokeResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message LeaseKeepAliveRequest {
|
||||
// ID is the lease ID for the lease to keep alive.
|
||||
int64 ID = 1;
|
||||
}
|
||||
|
||||
message LeaseKeepAliveResponse {
|
||||
ResponseHeader header = 1;
|
||||
// ID is the lease ID from the keep alive request.
|
||||
int64 ID = 2;
|
||||
// TTL is the new time-to-live for the lease.
|
||||
int64 TTL = 3;
|
||||
}
|
||||
|
||||
message Member {
|
||||
// ID is the member ID for this member.
|
||||
uint64 ID = 1;
|
||||
// name is the human-readable name of the member. If the member is not started, the name will be an empty string.
|
||||
string name = 2;
|
||||
// peerURLs is the list of URLs the member exposes to the cluster for communication.
|
||||
repeated string peerURLs = 3;
|
||||
// clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty.
|
||||
repeated string clientURLs = 4;
|
||||
}
|
||||
|
||||
message MemberAddRequest {
|
||||
// peerURLs is the list of URLs the added member will use to communicate with the cluster.
|
||||
repeated string peerURLs = 1;
|
||||
}
|
||||
|
||||
message MemberAddResponse {
|
||||
ResponseHeader header = 1;
|
||||
// member is the member information for the added member.
|
||||
Member member = 2;
|
||||
}
|
||||
|
||||
message MemberRemoveRequest {
|
||||
// ID is the member ID of the member to remove.
|
||||
uint64 ID = 1;
|
||||
}
|
||||
|
||||
message MemberRemoveResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message MemberUpdateRequest {
|
||||
// ID is the member ID of the member to update.
|
||||
uint64 ID = 1;
|
||||
// peerURLs is the new list of URLs the member will use to communicate with the cluster.
|
||||
repeated string peerURLs = 2;
|
||||
}
|
||||
|
||||
message MemberUpdateResponse{
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message MemberListRequest {
|
||||
}
|
||||
|
||||
message MemberListResponse {
|
||||
ResponseHeader header = 1;
|
||||
// members is a list of all members associated with the cluster.
|
||||
repeated Member members = 2;
|
||||
}
|
||||
|
||||
message DefragmentRequest {
|
||||
}
|
||||
|
||||
message DefragmentResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
enum AlarmType {
|
||||
NONE = 0; // default, used to query if any alarm is active
|
||||
NOSPACE = 1; // space quota is exhausted
|
||||
}
|
||||
|
||||
message AlarmRequest {
|
||||
enum AlarmAction {
|
||||
GET = 0;
|
||||
ACTIVATE = 1;
|
||||
DEACTIVATE = 2;
|
||||
}
|
||||
// action is the kind of alarm request to issue. The action
|
||||
// may GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a
|
||||
// raised alarm.
|
||||
AlarmAction action = 1;
|
||||
// memberID is the ID of the member associated with the alarm. If memberID is 0, the
|
||||
// alarm request covers all members.
|
||||
uint64 memberID = 2;
|
||||
// alarm is the type of alarm to consider for this request.
|
||||
AlarmType alarm = 3;
|
||||
}
|
||||
|
||||
message AlarmMember {
|
||||
// memberID is the ID of the member associated with the raised alarm.
|
||||
uint64 memberID = 1;
|
||||
// alarm is the type of alarm which has been raised.
|
||||
AlarmType alarm = 2;
|
||||
}
|
||||
|
||||
message AlarmResponse {
|
||||
ResponseHeader header = 1;
|
||||
// alarms is a list of alarms associated with the alarm request.
|
||||
repeated AlarmMember alarms = 2;
|
||||
}
|
||||
|
||||
message StatusRequest {
|
||||
}
|
||||
|
||||
message StatusResponse {
|
||||
ResponseHeader header = 1;
|
||||
// version is the cluster protocol version used by the responding member.
|
||||
string version = 2;
|
||||
// dbSize is the size of the backend database, in bytes, of the responding member.
|
||||
int64 dbSize = 3;
|
||||
// leader is the member ID which the responding member believes is the current leader.
|
||||
uint64 leader = 4;
|
||||
// raftIndex is the current raft index of the responding member.
|
||||
uint64 raftIndex = 5;
|
||||
// raftTerm is the current raft term of the responding member.
|
||||
uint64 raftTerm = 6;
|
||||
}
|
||||
|
||||
message AuthEnableRequest {
|
||||
}
|
||||
|
||||
message AuthDisableRequest {
|
||||
}
|
||||
|
||||
message AuthenticateRequest {
|
||||
string name = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message AuthUserAddRequest {
|
||||
string name = 1;
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message AuthUserGetRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message AuthUserDeleteRequest {
|
||||
// name is the name of the user to delete.
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message AuthUserChangePasswordRequest {
|
||||
// name is the name of the user whose password is being changed.
|
||||
string name = 1;
|
||||
// password is the new password for the user.
|
||||
string password = 2;
|
||||
}
|
||||
|
||||
message AuthUserGrantRoleRequest {
|
||||
// user is the name of the user which should be granted a given role.
|
||||
string user = 1;
|
||||
// role is the name of the role to grant to the user.
|
||||
string role = 2;
|
||||
}
|
||||
|
||||
message AuthUserRevokeRoleRequest {
|
||||
string name = 1;
|
||||
string role = 2;
|
||||
}
|
||||
|
||||
message AuthRoleAddRequest {
|
||||
// name is the name of the role to add to the authentication system.
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message AuthRoleGetRequest {
|
||||
string role = 1;
|
||||
}
|
||||
|
||||
message AuthUserListRequest {
|
||||
}
|
||||
|
||||
message AuthRoleListRequest {
|
||||
}
|
||||
|
||||
message AuthRoleDeleteRequest {
|
||||
string role = 1;
|
||||
}
|
||||
|
||||
message AuthRoleGrantPermissionRequest {
|
||||
// name is the name of the role which will be granted the permission.
|
||||
string name = 1;
|
||||
// perm is the permission to grant to the role.
|
||||
authpb.Permission perm = 2;
|
||||
}
|
||||
|
||||
message AuthRoleRevokePermissionRequest {
|
||||
string role = 1;
|
||||
string key = 2;
|
||||
string range_end = 3;
|
||||
}
|
||||
|
||||
message AuthEnableResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthDisableResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthenticateResponse {
|
||||
ResponseHeader header = 1;
|
||||
// token is an authorized token that can be used in succeeding RPCs
|
||||
string token = 2;
|
||||
}
|
||||
|
||||
message AuthUserAddResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthUserGetResponse {
|
||||
ResponseHeader header = 1;
|
||||
|
||||
repeated string roles = 2;
|
||||
}
|
||||
|
||||
message AuthUserDeleteResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthUserChangePasswordResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthUserGrantRoleResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthUserRevokeRoleResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthRoleAddResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthRoleGetResponse {
|
||||
ResponseHeader header = 1;
|
||||
|
||||
repeated authpb.Permission perm = 2;
|
||||
}
|
||||
|
||||
message AuthRoleListResponse {
|
||||
ResponseHeader header = 1;
|
||||
|
||||
repeated string roles = 2;
|
||||
}
|
||||
|
||||
message AuthUserListResponse {
|
||||
ResponseHeader header = 1;
|
||||
|
||||
repeated string users = 2;
|
||||
}
|
||||
|
||||
message AuthRoleDeleteResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthRoleGrantPermissionResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message AuthRoleRevokePermissionResponse {
|
||||
ResponseHeader header = 1;
|
||||
}
|
||||
+71
-21
@@ -20,9 +20,9 @@ import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
|
||||
math "math"
|
||||
)
|
||||
|
||||
import io "io"
|
||||
io "io"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -89,6 +89,8 @@ type Event struct {
|
||||
// A DELETE/EXPIRE event contains the deleted key with
|
||||
// its modification revision set to the revision of deletion.
|
||||
Kv *KeyValue `protobuf:"bytes,2,opt,name=kv" json:"kv,omitempty"`
|
||||
// prev_kv holds the key-value pair before the event happens.
|
||||
PrevKv *KeyValue `protobuf:"bytes,3,opt,name=prev_kv,json=prevKv" json:"prev_kv,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Event) Reset() { *m = Event{} }
|
||||
@@ -181,6 +183,16 @@ func (m *Event) MarshalTo(data []byte) (int, error) {
|
||||
}
|
||||
i += n1
|
||||
}
|
||||
if m.PrevKv != nil {
|
||||
data[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintKv(data, i, uint64(m.PrevKv.Size()))
|
||||
n2, err := m.PrevKv.MarshalTo(data[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i += n2
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -247,6 +259,10 @@ func (m *Event) Size() (n int) {
|
||||
l = m.Kv.Size()
|
||||
n += 1 + l + sovKv(uint64(l))
|
||||
}
|
||||
if m.PrevKv != nil {
|
||||
l = m.PrevKv.Size()
|
||||
n += 1 + l + sovKv(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -532,6 +548,39 @@ func (m *Event) Unmarshal(data []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PrevKv", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowKv
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := data[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthKv
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.PrevKv == nil {
|
||||
m.PrevKv = &KeyValue{}
|
||||
}
|
||||
if err := m.PrevKv.Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipKv(data[iNdEx:])
|
||||
@@ -659,23 +708,24 @@ var (
|
||||
)
|
||||
|
||||
var fileDescriptorKv = []byte{
|
||||
// 285 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x90, 0x41, 0x4a, 0xc3, 0x40,
|
||||
0x14, 0x86, 0x33, 0x4d, 0x9b, 0xd6, 0xd7, 0x52, 0xc3, 0x50, 0x70, 0x70, 0x31, 0xc4, 0x6e, 0x2c,
|
||||
0x08, 0x11, 0xea, 0x0d, 0xc4, 0xac, 0x74, 0x21, 0x21, 0xba, 0x95, 0x34, 0x7d, 0x94, 0x92, 0xa6,
|
||||
0x13, 0xd2, 0x38, 0x98, 0x9b, 0x78, 0x0a, 0xcf, 0xd1, 0x65, 0x8f, 0x60, 0xe3, 0x45, 0x24, 0x6f,
|
||||
0x4c, 0xdd, 0x0c, 0xef, 0xff, 0xff, 0x6f, 0x98, 0xff, 0x0d, 0x0c, 0x52, 0xed, 0xe7, 0x85, 0x2a,
|
||||
0x15, 0x77, 0x32, 0x9d, 0x24, 0xf9, 0xe2, 0x72, 0xb2, 0x52, 0x2b, 0x45, 0xd6, 0x6d, 0x33, 0x99,
|
||||
0x74, 0xfa, 0xc5, 0x60, 0xf0, 0x88, 0xd5, 0x6b, 0xbc, 0x79, 0x47, 0xee, 0x82, 0x9d, 0x62, 0x25,
|
||||
0x98, 0xc7, 0x66, 0xa3, 0xb0, 0x19, 0xf9, 0x35, 0x9c, 0x27, 0x05, 0xc6, 0x25, 0xbe, 0x15, 0xa8,
|
||||
0xd7, 0xbb, 0xb5, 0xda, 0x8a, 0x8e, 0xc7, 0x66, 0x76, 0x38, 0x36, 0x76, 0xf8, 0xe7, 0xf2, 0x2b,
|
||||
0x18, 0x65, 0x6a, 0xf9, 0x4f, 0xd9, 0x44, 0x0d, 0x33, 0xb5, 0x3c, 0x21, 0x02, 0xfa, 0x1a, 0x0b,
|
||||
0x4a, 0xbb, 0x94, 0xb6, 0x92, 0x4f, 0xa0, 0xa7, 0x9b, 0x02, 0xa2, 0x47, 0x2f, 0x1b, 0xd1, 0xb8,
|
||||
0x1b, 0x8c, 0x77, 0x28, 0x1c, 0xa2, 0x8d, 0x98, 0x7e, 0x40, 0x2f, 0xd0, 0xb8, 0x2d, 0xf9, 0x0d,
|
||||
0x74, 0xcb, 0x2a, 0x47, 0x6a, 0x3b, 0x9e, 0x5f, 0xf8, 0x66, 0x4d, 0x9f, 0x42, 0x73, 0x46, 0x55,
|
||||
0x8e, 0x21, 0x41, 0xdc, 0x83, 0x4e, 0xaa, 0xa9, 0xfa, 0x70, 0xee, 0xb6, 0x68, 0xbb, 0x77, 0xd8,
|
||||
0x49, 0xf5, 0xd4, 0x83, 0xb3, 0xd3, 0x25, 0xde, 0x07, 0xfb, 0xf9, 0x25, 0x72, 0x2d, 0x0e, 0xe0,
|
||||
0x3c, 0x04, 0x4f, 0x41, 0x14, 0xb8, 0xec, 0x5e, 0xec, 0x8f, 0xd2, 0x3a, 0x1c, 0xa5, 0xb5, 0xaf,
|
||||
0x25, 0x3b, 0xd4, 0x92, 0x7d, 0xd7, 0x92, 0x7d, 0xfe, 0x48, 0x6b, 0xe1, 0xd0, 0x5f, 0xde, 0xfd,
|
||||
0x06, 0x00, 0x00, 0xff, 0xff, 0xd6, 0x21, 0x8f, 0x2c, 0x75, 0x01, 0x00, 0x00,
|
||||
// 303 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x90, 0x41, 0x4e, 0xc2, 0x40,
|
||||
0x14, 0x86, 0x3b, 0x14, 0x0a, 0x3e, 0x08, 0x36, 0x13, 0x12, 0x27, 0x2e, 0x26, 0x95, 0x8d, 0x18,
|
||||
0x13, 0x4c, 0xf0, 0x06, 0xc6, 0xae, 0x70, 0x61, 0x1a, 0x74, 0x4b, 0x4a, 0x79, 0x21, 0xa4, 0x94,
|
||||
0x69, 0x4a, 0x9d, 0xa4, 0x37, 0x71, 0xef, 0xde, 0x73, 0xb0, 0xe4, 0x08, 0x52, 0x2f, 0x62, 0xfa,
|
||||
0xc6, 0xe2, 0xc6, 0xcd, 0xe4, 0xfd, 0xff, 0xff, 0x65, 0xe6, 0x7f, 0x03, 0x9d, 0x58, 0x8f, 0xd3,
|
||||
0x4c, 0xe5, 0x8a, 0x3b, 0x89, 0x8e, 0xa2, 0x74, 0x71, 0x39, 0x58, 0xa9, 0x95, 0x22, 0xeb, 0xae,
|
||||
0x9a, 0x4c, 0x3a, 0xfc, 0x64, 0xd0, 0x99, 0x62, 0xf1, 0x1a, 0x6e, 0xde, 0x90, 0xbb, 0x60, 0xc7,
|
||||
0x58, 0x08, 0xe6, 0xb1, 0x51, 0x2f, 0xa8, 0x46, 0x7e, 0x0d, 0xe7, 0x51, 0x86, 0x61, 0x8e, 0xf3,
|
||||
0x0c, 0xf5, 0x7a, 0xb7, 0x56, 0x5b, 0xd1, 0xf0, 0xd8, 0xc8, 0x0e, 0xfa, 0xc6, 0x0e, 0x7e, 0x5d,
|
||||
0x7e, 0x05, 0xbd, 0x44, 0x2d, 0xff, 0x28, 0x9b, 0xa8, 0x6e, 0xa2, 0x96, 0x27, 0x44, 0x40, 0x5b,
|
||||
0x63, 0x46, 0x69, 0x93, 0xd2, 0x5a, 0xf2, 0x01, 0xb4, 0x74, 0x55, 0x40, 0xb4, 0xe8, 0x65, 0x23,
|
||||
0x2a, 0x77, 0x83, 0xe1, 0x0e, 0x85, 0x43, 0xb4, 0x11, 0xc3, 0x0f, 0x06, 0x2d, 0x5f, 0xe3, 0x36,
|
||||
0xe7, 0xb7, 0xd0, 0xcc, 0x8b, 0x14, 0xa9, 0x6e, 0x7f, 0x72, 0x31, 0x36, 0x7b, 0x8e, 0x29, 0x34,
|
||||
0xe7, 0xac, 0x48, 0x31, 0x20, 0x88, 0x7b, 0xd0, 0x88, 0x35, 0x75, 0xef, 0x4e, 0xdc, 0x1a, 0xad,
|
||||
0x17, 0x0f, 0x1a, 0xb1, 0xe6, 0x37, 0xd0, 0x4e, 0x33, 0xd4, 0xf3, 0x58, 0x53, 0xf9, 0xff, 0x30,
|
||||
0xa7, 0x02, 0xa6, 0x7a, 0xe8, 0xc1, 0xd9, 0xe9, 0x7e, 0xde, 0x06, 0xfb, 0xf9, 0x65, 0xe6, 0x5a,
|
||||
0x1c, 0xc0, 0x79, 0xf4, 0x9f, 0xfc, 0x99, 0xef, 0xb2, 0x07, 0xb1, 0x3f, 0x4a, 0xeb, 0x70, 0x94,
|
||||
0xd6, 0xbe, 0x94, 0xec, 0x50, 0x4a, 0xf6, 0x55, 0x4a, 0xf6, 0xfe, 0x2d, 0xad, 0x85, 0x43, 0xff,
|
||||
0x7e, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x45, 0x92, 0x5d, 0xa1, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package mvccpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.sizer_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
option (gogoproto.goproto_getters_all) = false;
|
||||
option (gogoproto.goproto_enum_prefix_all) = false;
|
||||
|
||||
message KeyValue {
|
||||
// key is the key in bytes. An empty key is not allowed.
|
||||
bytes key = 1;
|
||||
// create_revision is the revision of last creation on this key.
|
||||
int64 create_revision = 2;
|
||||
// mod_revision is the revision of last modification on this key.
|
||||
int64 mod_revision = 3;
|
||||
// version is the version of the key. A deletion resets
|
||||
// the version to zero and any modification of the key
|
||||
// increases its version.
|
||||
int64 version = 4;
|
||||
// value is the value held by the key, in bytes.
|
||||
bytes value = 5;
|
||||
// lease is the ID of the lease that attached to key.
|
||||
// When the attached lease expires, the key will be deleted.
|
||||
// If lease is 0, then no lease is attached to the key.
|
||||
int64 lease = 6;
|
||||
}
|
||||
|
||||
message Event {
|
||||
enum EventType {
|
||||
PUT = 0;
|
||||
DELETE = 1;
|
||||
}
|
||||
// type is the kind of event. If type is a PUT, it indicates
|
||||
// new data has been stored to the key. If type is a DELETE,
|
||||
// it indicates the key was deleted.
|
||||
EventType type = 1;
|
||||
// kv holds the KeyValue for the event.
|
||||
// A PUT event contains current kv pair.
|
||||
// A PUT event with kv.Version=1 indicates the creation of a key.
|
||||
// A DELETE/EXPIRE event contains the deleted key with
|
||||
// its modification revision set to the revision of deletion.
|
||||
KeyValue kv = 2;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package fileutil
|
||||
|
||||
import "os"
|
||||
|
||||
// OpenDir opens a directory for syncing.
|
||||
func OpenDir(path string) (*os.File, error) { return os.Open(path) }
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
// +build windows
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// OpenDir opens a directory in windows with write access for syncing.
|
||||
func OpenDir(path string) (*os.File, error) {
|
||||
fd, err := openDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fd), path), nil
|
||||
}
|
||||
|
||||
func openDir(path string) (fd syscall.Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE)
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
|
||||
createmode := uint32(syscall.OPEN_EXISTING)
|
||||
fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0)
|
||||
}
|
||||
+24
-1
@@ -33,7 +33,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "fileutil")
|
||||
plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "pkg/fileutil")
|
||||
)
|
||||
|
||||
// IsDirWriteable checks if dir is writable by writing and removing a file
|
||||
@@ -96,3 +96,26 @@ func Exist(name string) bool {
|
||||
_, err := os.Stat(name)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
|
||||
// shorten the length of the file.
|
||||
func ZeroToEnd(f *os.File) error {
|
||||
// TODO: support FALLOC_FL_ZERO_RANGE
|
||||
off, err := f.Seek(0, os.SEEK_CUR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lenf, lerr := f.Seek(0, os.SEEK_END)
|
||||
if lerr != nil {
|
||||
return lerr
|
||||
}
|
||||
if err = f.Truncate(off); err != nil {
|
||||
return err
|
||||
}
|
||||
// make sure blocks remain allocated
|
||||
if err = Preallocate(f, lenf, true); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Seek(off, os.SEEK_SET)
|
||||
return err
|
||||
}
|
||||
|
||||
+22
-16
@@ -34,27 +34,30 @@ import (
|
||||
"github.com/coreos/etcd/pkg/tlsutil"
|
||||
)
|
||||
|
||||
func NewListener(addr string, scheme string, tlscfg *tls.Config) (l net.Listener, err error) {
|
||||
if scheme == "unix" || scheme == "unixs" {
|
||||
// unix sockets via unix://laddr
|
||||
l, err = NewUnixListener(addr)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
func NewListener(addr, scheme string, tlscfg *tls.Config) (l net.Listener, err error) {
|
||||
if l, err = newListener(addr, scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return wrapTLS(addr, scheme, tlscfg, l)
|
||||
}
|
||||
|
||||
if scheme == "https" || scheme == "unixs" {
|
||||
if tlscfg == nil {
|
||||
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
|
||||
}
|
||||
|
||||
l = tls.NewListener(l, tlscfg)
|
||||
func newListener(addr string, scheme string) (net.Listener, error) {
|
||||
if scheme == "unix" || scheme == "unixs" {
|
||||
// unix sockets via unix://laddr
|
||||
return NewUnixListener(addr)
|
||||
}
|
||||
return net.Listen("tcp", addr)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
func wrapTLS(addr, scheme string, tlscfg *tls.Config, l net.Listener) (net.Listener, error) {
|
||||
if scheme != "https" && scheme != "unixs" {
|
||||
return l, nil
|
||||
}
|
||||
if tlscfg == nil {
|
||||
l.Close()
|
||||
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
|
||||
}
|
||||
return tls.NewListener(l, tlscfg), nil
|
||||
}
|
||||
|
||||
type TLSInfo struct {
|
||||
@@ -205,6 +208,9 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
||||
cfg.ClientCAs = cp
|
||||
}
|
||||
|
||||
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
|
||||
cfg.NextProtos = []string{"h2"}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -24,15 +24,19 @@ import (
|
||||
// If read/write on the accepted connection blocks longer than its time limit,
|
||||
// it will return timeout error.
|
||||
func NewTimeoutListener(addr string, scheme string, tlscfg *tls.Config, rdtimeoutd, wtimeoutd time.Duration) (net.Listener, error) {
|
||||
ln, err := NewListener(addr, scheme, tlscfg)
|
||||
ln, err := newListener(addr, scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rwTimeoutListener{
|
||||
ln = &rwTimeoutListener{
|
||||
Listener: ln,
|
||||
rdtimeoutd: rdtimeoutd,
|
||||
wtimeoutd: wtimeoutd,
|
||||
}, nil
|
||||
}
|
||||
if ln, err = wrapTLS(addr, scheme, tlscfg, ln); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
type rwTimeoutListener struct {
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.
|
||||
// it should not be put back to http transport as an idle connection for future usage.
|
||||
tr.MaxIdleConnsPerHost = -1
|
||||
} else {
|
||||
// allow more idle connections between peers to avoid unncessary port allocation.
|
||||
// allow more idle connections between peers to avoid unnecessary port allocation.
|
||||
tr.MaxIdleConnsPerHost = 1024
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -64,7 +64,8 @@ func NewTransport(info TLSInfo, dialtimeoutd time.Duration) (*http.Transport, er
|
||||
}
|
||||
|
||||
func (urt *unixTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req2 := *req
|
||||
req2.URL.Scheme = strings.Replace(req.URL.Scheme, "unix", "http", 1)
|
||||
url := *req.URL
|
||||
req.URL = &url
|
||||
req.URL.Scheme = strings.Replace(req.URL.Scheme, "unix", "http", 1)
|
||||
return urt.Transport.RoundTrip(req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user