mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 02:33:34 +00:00
rebase: bump k8s.io/kubernetes from 1.26.2 to 1.27.2
Bumps [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes) from 1.26.2 to 1.27.2. - [Release notes](https://github.com/kubernetes/kubernetes/releases) - [Commits](https://github.com/kubernetes/kubernetes/compare/v1.26.2...v1.27.2) --- updated-dependencies: - dependency-name: k8s.io/kubernetes dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
mergify[bot]
parent
0e79135419
commit
07b05616a0
202
vendor/go.etcd.io/etcd/client/v3/LICENSE
generated
vendored
Normal file
202
vendor/go.etcd.io/etcd/client/v3/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
92
vendor/go.etcd.io/etcd/client/v3/README.md
generated
vendored
Normal file
92
vendor/go.etcd.io/etcd/client/v3/README.md
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
# etcd/clientv3
|
||||
|
||||
[](https://etcd.io/docs)
|
||||
[](https://godoc.org/go.etcd.io/etcd/clientv3)
|
||||
|
||||
`etcd/clientv3` is the official Go etcd client for v3.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get go.etcd.io/etcd/client/v3
|
||||
```
|
||||
|
||||
Warning: As etcd 3.5.0 was not yet released, the command above does not work.
|
||||
After first pre-release of 3.5.0 [#12498](https://github.com/etcd-io/etcd/issues/12498),
|
||||
etcd can be referenced using:
|
||||
```
|
||||
go get go.etcd.io/etcd/client/v3@v3.5.0-pre
|
||||
```
|
||||
|
||||
## 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`](https://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 := cli.Put(ctx, "sample_key", "sample_value")
|
||||
cancel()
|
||||
if err != nil {
|
||||
// handle error!
|
||||
}
|
||||
// use the response
|
||||
```
|
||||
|
||||
For full compatibility, it is recommended to install released versions of clients using go modules.
|
||||
|
||||
## 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/go.etcd.io/etcd/api/v3rpc/rpctypes).
|
||||
|
||||
Here is the example code to handle client errors:
|
||||
|
||||
```go
|
||||
resp, err := cli.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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
The etcd client optionally exposes RPC metrics through [go-grpc-prometheus](https://github.com/grpc-ecosystem/go-grpc-prometheus). See the [examples](https://github.com/etcd-io/etcd/blob/main/tests/integration/clientv3/examples/example_metrics_test.go).
|
||||
|
||||
## Namespacing
|
||||
|
||||
The [namespace](https://godoc.org/go.etcd.io/etcd/client/v3/namespace) package provides `clientv3` interface wrappers to transparently isolate client requests to a user-defined prefix.
|
||||
|
||||
## Request size limit
|
||||
|
||||
Client request size limit is configurable via `clientv3.Config.MaxCallSendMsgSize` and `MaxCallRecvMsgSize` in bytes. If none given, client request send limit defaults to 2 MiB including gRPC overhead bytes. And receive limit defaults to `math.MaxInt32`.
|
||||
|
||||
## Examples
|
||||
|
||||
More code [examples](https://github.com/etcd-io/etcd/tree/main/tests/integration/clientv3/examples) can be found at [GoDoc](https://pkg.go.dev/go.etcd.io/etcd/client/v3).
|
236
vendor/go.etcd.io/etcd/client/v3/auth.go
generated
vendored
Normal file
236
vendor/go.etcd.io/etcd/client/v3/auth.go
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/authpb"
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type (
|
||||
AuthEnableResponse pb.AuthEnableResponse
|
||||
AuthDisableResponse pb.AuthDisableResponse
|
||||
AuthStatusResponse pb.AuthStatusResponse
|
||||
AuthenticateResponse pb.AuthenticateResponse
|
||||
AuthUserAddResponse pb.AuthUserAddResponse
|
||||
AuthUserDeleteResponse pb.AuthUserDeleteResponse
|
||||
AuthUserChangePasswordResponse pb.AuthUserChangePasswordResponse
|
||||
AuthUserGrantRoleResponse pb.AuthUserGrantRoleResponse
|
||||
AuthUserGetResponse pb.AuthUserGetResponse
|
||||
AuthUserRevokeRoleResponse pb.AuthUserRevokeRoleResponse
|
||||
AuthRoleAddResponse pb.AuthRoleAddResponse
|
||||
AuthRoleGrantPermissionResponse pb.AuthRoleGrantPermissionResponse
|
||||
AuthRoleGetResponse pb.AuthRoleGetResponse
|
||||
AuthRoleRevokePermissionResponse pb.AuthRoleRevokePermissionResponse
|
||||
AuthRoleDeleteResponse pb.AuthRoleDeleteResponse
|
||||
AuthUserListResponse pb.AuthUserListResponse
|
||||
AuthRoleListResponse pb.AuthRoleListResponse
|
||||
|
||||
PermissionType authpb.Permission_Type
|
||||
Permission authpb.Permission
|
||||
)
|
||||
|
||||
const (
|
||||
PermRead = authpb.READ
|
||||
PermWrite = authpb.WRITE
|
||||
PermReadWrite = authpb.READWRITE
|
||||
)
|
||||
|
||||
type UserAddOptions authpb.UserAddOptions
|
||||
|
||||
type Auth interface {
|
||||
// Authenticate login and get token
|
||||
Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error)
|
||||
|
||||
// AuthEnable enables auth of an etcd cluster.
|
||||
AuthEnable(ctx context.Context) (*AuthEnableResponse, error)
|
||||
|
||||
// AuthDisable disables auth of an etcd cluster.
|
||||
AuthDisable(ctx context.Context) (*AuthDisableResponse, error)
|
||||
|
||||
// AuthStatus returns the status of auth of an etcd cluster.
|
||||
AuthStatus(ctx context.Context) (*AuthStatusResponse, error)
|
||||
|
||||
// UserAdd adds a new user to an etcd cluster.
|
||||
UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error)
|
||||
|
||||
// UserAddWithOptions adds a new user to an etcd cluster with some options.
|
||||
UserAddWithOptions(ctx context.Context, name string, password string, opt *UserAddOptions) (*AuthUserAddResponse, error)
|
||||
|
||||
// UserDelete deletes a user from an etcd cluster.
|
||||
UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error)
|
||||
|
||||
// UserChangePassword changes a password of a user.
|
||||
UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error)
|
||||
|
||||
// UserGrantRole grants a role to a user.
|
||||
UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error)
|
||||
|
||||
// UserGet gets a detailed information of a user.
|
||||
UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error)
|
||||
|
||||
// UserList gets a list of all users.
|
||||
UserList(ctx context.Context) (*AuthUserListResponse, error)
|
||||
|
||||
// UserRevokeRole revokes a role of a user.
|
||||
UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error)
|
||||
|
||||
// RoleAdd adds a new role to an etcd cluster.
|
||||
RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error)
|
||||
|
||||
// RoleGrantPermission grants a permission to a role.
|
||||
RoleGrantPermission(ctx context.Context, name string, key, rangeEnd string, permType PermissionType) (*AuthRoleGrantPermissionResponse, error)
|
||||
|
||||
// RoleGet gets a detailed information of a role.
|
||||
RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error)
|
||||
|
||||
// RoleList gets a list of all roles.
|
||||
RoleList(ctx context.Context) (*AuthRoleListResponse, error)
|
||||
|
||||
// RoleRevokePermission revokes a permission from a role.
|
||||
RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error)
|
||||
|
||||
// RoleDelete deletes a role.
|
||||
RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error)
|
||||
}
|
||||
|
||||
type authClient struct {
|
||||
remote pb.AuthClient
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
func NewAuth(c *Client) Auth {
|
||||
api := &authClient{remote: RetryAuthClient(c)}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func NewAuthFromAuthClient(remote pb.AuthClient, c *Client) Auth {
|
||||
api := &authClient{remote: remote}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func (auth *authClient) Authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
|
||||
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, auth.callOpts...)
|
||||
return (*AuthenticateResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
|
||||
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}, auth.callOpts...)
|
||||
return (*AuthEnableResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthDisable(ctx context.Context) (*AuthDisableResponse, error) {
|
||||
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{}, auth.callOpts...)
|
||||
return (*AuthDisableResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthStatus(ctx context.Context) (*AuthStatusResponse, error) {
|
||||
resp, err := auth.remote.AuthStatus(ctx, &pb.AuthStatusRequest{}, auth.callOpts...)
|
||||
return (*AuthStatusResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error) {
|
||||
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password, Options: &authpb.UserAddOptions{NoPassword: false}}, auth.callOpts...)
|
||||
return (*AuthUserAddResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserAddWithOptions(ctx context.Context, name string, password string, options *UserAddOptions) (*AuthUserAddResponse, error) {
|
||||
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password, Options: (*authpb.UserAddOptions)(options)}, auth.callOpts...)
|
||||
return (*AuthUserAddResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error) {
|
||||
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name}, auth.callOpts...)
|
||||
return (*AuthUserDeleteResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error) {
|
||||
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password}, auth.callOpts...)
|
||||
return (*AuthUserChangePasswordResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error) {
|
||||
resp, err := auth.remote.UserGrantRole(ctx, &pb.AuthUserGrantRoleRequest{User: user, Role: role}, auth.callOpts...)
|
||||
return (*AuthUserGrantRoleResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error) {
|
||||
resp, err := auth.remote.UserGet(ctx, &pb.AuthUserGetRequest{Name: name}, auth.callOpts...)
|
||||
return (*AuthUserGetResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserList(ctx context.Context) (*AuthUserListResponse, error) {
|
||||
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{}, auth.callOpts...)
|
||||
return (*AuthUserListResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error) {
|
||||
resp, err := auth.remote.UserRevokeRole(ctx, &pb.AuthUserRevokeRoleRequest{Name: name, Role: role}, auth.callOpts...)
|
||||
return (*AuthUserRevokeRoleResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
|
||||
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name}, auth.callOpts...)
|
||||
return (*AuthRoleAddResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleGrantPermission(ctx context.Context, name string, key, rangeEnd string, permType PermissionType) (*AuthRoleGrantPermissionResponse, error) {
|
||||
perm := &authpb.Permission{
|
||||
Key: []byte(key),
|
||||
RangeEnd: []byte(rangeEnd),
|
||||
PermType: authpb.Permission_Type(permType),
|
||||
}
|
||||
resp, err := auth.remote.RoleGrantPermission(ctx, &pb.AuthRoleGrantPermissionRequest{Name: name, Perm: perm}, auth.callOpts...)
|
||||
return (*AuthRoleGrantPermissionResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error) {
|
||||
resp, err := auth.remote.RoleGet(ctx, &pb.AuthRoleGetRequest{Role: role}, auth.callOpts...)
|
||||
return (*AuthRoleGetResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleList(ctx context.Context) (*AuthRoleListResponse, error) {
|
||||
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{}, auth.callOpts...)
|
||||
return (*AuthRoleListResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error) {
|
||||
resp, err := auth.remote.RoleRevokePermission(ctx, &pb.AuthRoleRevokePermissionRequest{Role: role, Key: []byte(key), RangeEnd: []byte(rangeEnd)}, auth.callOpts...)
|
||||
return (*AuthRoleRevokePermissionResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error) {
|
||||
resp, err := auth.remote.RoleDelete(ctx, &pb.AuthRoleDeleteRequest{Role: role}, auth.callOpts...)
|
||||
return (*AuthRoleDeleteResponse)(resp), toErr(ctx, err)
|
||||
}
|
||||
|
||||
func StrToPermissionType(s string) (PermissionType, error) {
|
||||
val, ok := authpb.Permission_Type_value[strings.ToUpper(s)]
|
||||
if ok {
|
||||
return PermissionType(val), nil
|
||||
}
|
||||
return PermissionType(-1), fmt.Errorf("invalid permission type: %s", s)
|
||||
}
|
611
vendor/go.etcd.io/etcd/client/v3/client.go
generated
vendored
Normal file
611
vendor/go.etcd.io/etcd/client/v3/client.go
generated
vendored
Normal file
@ -0,0 +1,611 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||
"go.etcd.io/etcd/client/v3/credentials"
|
||||
"go.etcd.io/etcd/client/v3/internal/endpoint"
|
||||
"go.etcd.io/etcd/client/v3/internal/resolver"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
grpccredentials "google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoAvailableEndpoints = errors.New("etcdclient: no available endpoints")
|
||||
ErrOldCluster = errors.New("etcdclient: old cluster version")
|
||||
)
|
||||
|
||||
// Client provides and manages an etcd v3 client session.
|
||||
type Client struct {
|
||||
Cluster
|
||||
KV
|
||||
Lease
|
||||
Watcher
|
||||
Auth
|
||||
Maintenance
|
||||
|
||||
conn *grpc.ClientConn
|
||||
|
||||
cfg Config
|
||||
creds grpccredentials.TransportCredentials
|
||||
resolver *resolver.EtcdManualResolver
|
||||
mu *sync.RWMutex
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// Username is a user name for authentication.
|
||||
Username string
|
||||
// Password is a password for authentication.
|
||||
Password string
|
||||
authTokenBundle credentials.Bundle
|
||||
|
||||
callOpts []grpc.CallOption
|
||||
|
||||
lgMu *sync.RWMutex
|
||||
lg *zap.Logger
|
||||
}
|
||||
|
||||
// New creates a new etcdv3 client from a given configuration.
|
||||
func New(cfg Config) (*Client, error) {
|
||||
if len(cfg.Endpoints) == 0 {
|
||||
return nil, ErrNoAvailableEndpoints
|
||||
}
|
||||
|
||||
return newClient(&cfg)
|
||||
}
|
||||
|
||||
// NewCtxClient creates a client with a context but no underlying grpc
|
||||
// connection. This is useful for embedded cases that override the
|
||||
// service interface implementations and do not need connection management.
|
||||
func NewCtxClient(ctx context.Context, opts ...Option) *Client {
|
||||
cctx, cancel := context.WithCancel(ctx)
|
||||
c := &Client{ctx: cctx, cancel: cancel, lgMu: new(sync.RWMutex)}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
if c.lg == nil {
|
||||
c.lg = zap.NewNop()
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Option is a function type that can be passed as argument to NewCtxClient to configure client
|
||||
type Option func(*Client)
|
||||
|
||||
// NewFromURL creates a new etcdv3 client from a URL.
|
||||
func NewFromURL(url string) (*Client, error) {
|
||||
return New(Config{Endpoints: []string{url}})
|
||||
}
|
||||
|
||||
// NewFromURLs creates a new etcdv3 client from URLs.
|
||||
func NewFromURLs(urls []string) (*Client, error) {
|
||||
return New(Config{Endpoints: urls})
|
||||
}
|
||||
|
||||
// WithZapLogger is a NewCtxClient option that overrides the logger
|
||||
func WithZapLogger(lg *zap.Logger) Option {
|
||||
return func(c *Client) {
|
||||
c.lg = lg
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger overrides the logger.
|
||||
//
|
||||
// Deprecated: Please use WithZapLogger or Logger field in clientv3.Config
|
||||
//
|
||||
// Does not changes grpcLogger, that can be explicitly configured
|
||||
// using grpc_zap.ReplaceGrpcLoggerV2(..) method.
|
||||
func (c *Client) WithLogger(lg *zap.Logger) *Client {
|
||||
c.lgMu.Lock()
|
||||
c.lg = lg
|
||||
c.lgMu.Unlock()
|
||||
return c
|
||||
}
|
||||
|
||||
// GetLogger gets the logger.
|
||||
// NOTE: This method is for internal use of etcd-client library and should not be used as general-purpose logger.
|
||||
func (c *Client) GetLogger() *zap.Logger {
|
||||
c.lgMu.RLock()
|
||||
l := c.lg
|
||||
c.lgMu.RUnlock()
|
||||
return l
|
||||
}
|
||||
|
||||
// Close shuts down the client's etcd connections.
|
||||
func (c *Client) Close() error {
|
||||
c.cancel()
|
||||
if c.Watcher != nil {
|
||||
c.Watcher.Close()
|
||||
}
|
||||
if c.Lease != nil {
|
||||
c.Lease.Close()
|
||||
}
|
||||
if c.conn != nil {
|
||||
return toErr(c.ctx, c.conn.Close())
|
||||
}
|
||||
return c.ctx.Err()
|
||||
}
|
||||
|
||||
// Ctx is a context for "out of band" messages (e.g., for sending
|
||||
// "clean up" message when another context is canceled). It is
|
||||
// canceled on client Close().
|
||||
func (c *Client) Ctx() context.Context { return c.ctx }
|
||||
|
||||
// Endpoints lists the registered endpoints for the client.
|
||||
func (c *Client) Endpoints() []string {
|
||||
// copy the slice; protect original endpoints from being changed
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
eps := make([]string, len(c.cfg.Endpoints))
|
||||
copy(eps, c.cfg.Endpoints)
|
||||
return eps
|
||||
}
|
||||
|
||||
// SetEndpoints updates client's endpoints.
|
||||
func (c *Client) SetEndpoints(eps ...string) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.cfg.Endpoints = eps
|
||||
|
||||
c.resolver.SetEndpoints(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 {
|
||||
if len(m.Name) != 0 && !m.IsLearner {
|
||||
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, cancel := context.WithTimeout(c.ctx, 5*time.Second)
|
||||
err := c.Sync(ctx)
|
||||
cancel()
|
||||
if err != nil && err != c.ctx.Err() {
|
||||
c.lg.Info("Auto sync endpoints failed.", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dialSetupOpts gives the dial opts prior to any authentication.
|
||||
func (c *Client) dialSetupOpts(creds grpccredentials.TransportCredentials, dopts ...grpc.DialOption) (opts []grpc.DialOption, err error) {
|
||||
if c.cfg.DialKeepAliveTime > 0 {
|
||||
params := keepalive.ClientParameters{
|
||||
Time: c.cfg.DialKeepAliveTime,
|
||||
Timeout: c.cfg.DialKeepAliveTimeout,
|
||||
PermitWithoutStream: c.cfg.PermitWithoutStream,
|
||||
}
|
||||
opts = append(opts, grpc.WithKeepaliveParams(params))
|
||||
}
|
||||
opts = append(opts, dopts...)
|
||||
|
||||
if creds != nil {
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
} else {
|
||||
opts = append(opts, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
// Interceptor retry and backoff.
|
||||
// TODO: Replace all of clientv3/retry.go with RetryPolicy:
|
||||
// https://github.com/grpc/grpc-proto/blob/cdd9ed5c3d3f87aef62f373b93361cf7bddc620d/grpc/service_config/service_config.proto#L130
|
||||
rrBackoff := withBackoff(c.roundRobinQuorumBackoff(defaultBackoffWaitBetween, defaultBackoffJitterFraction))
|
||||
opts = append(opts,
|
||||
// Disable stream retry by default since go-grpc-middleware/retry does not support client streams.
|
||||
// Streams that are safe to retry are enabled individually.
|
||||
grpc.WithStreamInterceptor(c.streamClientInterceptor(withMax(0), rrBackoff)),
|
||||
grpc.WithUnaryInterceptor(c.unaryClientInterceptor(withMax(defaultUnaryMaxRetries), rrBackoff)),
|
||||
)
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// Dial connects to a single endpoint using the client's config.
|
||||
func (c *Client) Dial(ep string) (*grpc.ClientConn, error) {
|
||||
creds := c.credentialsForEndpoint(ep)
|
||||
|
||||
// Using ad-hoc created resolver, to guarantee only explicitly given
|
||||
// endpoint is used.
|
||||
return c.dial(creds, grpc.WithResolvers(resolver.New(ep)))
|
||||
}
|
||||
|
||||
func (c *Client) getToken(ctx context.Context) error {
|
||||
var err error // return last error in a case of fail
|
||||
|
||||
if c.Username == "" || c.Password == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := c.Auth.Authenticate(ctx, c.Username, c.Password)
|
||||
if err != nil {
|
||||
if err == rpctypes.ErrAuthNotEnabled {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
c.authTokenBundle.UpdateAuthToken(resp.Token)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dialWithBalancer dials the client's current load balanced resolver group. The scheme of the host
|
||||
// of the provided endpoint determines the scheme used for all endpoints of the client connection.
|
||||
func (c *Client) dialWithBalancer(dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
creds := c.credentialsForEndpoint(c.Endpoints()[0])
|
||||
opts := append(dopts, grpc.WithResolvers(c.resolver))
|
||||
return c.dial(creds, opts...)
|
||||
}
|
||||
|
||||
// dial configures and dials any grpc balancer target.
|
||||
func (c *Client) dial(creds grpccredentials.TransportCredentials, dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
opts, err := c.dialSetupOpts(creds, dopts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure dialer: %v", err)
|
||||
}
|
||||
if c.authTokenBundle != nil {
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(c.authTokenBundle.PerRPCCredentials()))
|
||||
}
|
||||
|
||||
opts = append(opts, c.cfg.DialOptions...)
|
||||
|
||||
dctx := c.ctx
|
||||
if c.cfg.DialTimeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
dctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
|
||||
defer cancel() // TODO: Is this right for cases where grpc.WithBlock() is not set on the dial options?
|
||||
}
|
||||
target := fmt.Sprintf("%s://%p/%s", resolver.Schema, c, authority(c.Endpoints()[0]))
|
||||
conn, err := grpc.DialContext(dctx, target, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func authority(endpoint string) string {
|
||||
spl := strings.SplitN(endpoint, "://", 2)
|
||||
if len(spl) < 2 {
|
||||
if strings.HasPrefix(endpoint, "unix:") {
|
||||
return endpoint[len("unix:"):]
|
||||
}
|
||||
if strings.HasPrefix(endpoint, "unixs:") {
|
||||
return endpoint[len("unixs:"):]
|
||||
}
|
||||
return endpoint
|
||||
}
|
||||
return spl[1]
|
||||
}
|
||||
|
||||
func (c *Client) credentialsForEndpoint(ep string) grpccredentials.TransportCredentials {
|
||||
r := endpoint.RequiresCredentials(ep)
|
||||
switch r {
|
||||
case endpoint.CREDS_DROP:
|
||||
return nil
|
||||
case endpoint.CREDS_OPTIONAL:
|
||||
return c.creds
|
||||
case endpoint.CREDS_REQUIRE:
|
||||
if c.creds != nil {
|
||||
return c.creds
|
||||
}
|
||||
return credentials.NewBundle(credentials.Config{}).TransportCredentials()
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported CredsRequirement: %v", r))
|
||||
}
|
||||
}
|
||||
|
||||
func newClient(cfg *Config) (*Client, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{}
|
||||
}
|
||||
var creds grpccredentials.TransportCredentials
|
||||
if cfg.TLS != nil {
|
||||
creds = credentials.NewBundle(credentials.Config{TLSConfig: cfg.TLS}).TransportCredentials()
|
||||
}
|
||||
|
||||
// use a temporary skeleton client to bootstrap first connection
|
||||
baseCtx := context.TODO()
|
||||
if cfg.Context != nil {
|
||||
baseCtx = cfg.Context
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(baseCtx)
|
||||
client := &Client{
|
||||
conn: nil,
|
||||
cfg: *cfg,
|
||||
creds: creds,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
mu: new(sync.RWMutex),
|
||||
callOpts: defaultCallOpts,
|
||||
lgMu: new(sync.RWMutex),
|
||||
}
|
||||
|
||||
var err error
|
||||
if cfg.Logger != nil {
|
||||
client.lg = cfg.Logger
|
||||
} else if cfg.LogConfig != nil {
|
||||
client.lg, err = cfg.LogConfig.Build()
|
||||
} else {
|
||||
client.lg, err = logutil.CreateDefaultZapLogger(etcdClientDebugLevel())
|
||||
if client.lg != nil {
|
||||
client.lg = client.lg.Named("etcd-client")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.Username != "" && cfg.Password != "" {
|
||||
client.Username = cfg.Username
|
||||
client.Password = cfg.Password
|
||||
client.authTokenBundle = credentials.NewBundle(credentials.Config{})
|
||||
}
|
||||
if cfg.MaxCallSendMsgSize > 0 || cfg.MaxCallRecvMsgSize > 0 {
|
||||
if cfg.MaxCallRecvMsgSize > 0 && cfg.MaxCallSendMsgSize > cfg.MaxCallRecvMsgSize {
|
||||
return nil, fmt.Errorf("gRPC message recv limit (%d bytes) must be greater than send limit (%d bytes)", cfg.MaxCallRecvMsgSize, cfg.MaxCallSendMsgSize)
|
||||
}
|
||||
callOpts := []grpc.CallOption{
|
||||
defaultWaitForReady,
|
||||
defaultMaxCallSendMsgSize,
|
||||
defaultMaxCallRecvMsgSize,
|
||||
}
|
||||
if cfg.MaxCallSendMsgSize > 0 {
|
||||
callOpts[1] = grpc.MaxCallSendMsgSize(cfg.MaxCallSendMsgSize)
|
||||
}
|
||||
if cfg.MaxCallRecvMsgSize > 0 {
|
||||
callOpts[2] = grpc.MaxCallRecvMsgSize(cfg.MaxCallRecvMsgSize)
|
||||
}
|
||||
client.callOpts = callOpts
|
||||
}
|
||||
|
||||
client.resolver = resolver.New(cfg.Endpoints...)
|
||||
|
||||
if len(cfg.Endpoints) < 1 {
|
||||
client.cancel()
|
||||
return nil, fmt.Errorf("at least one Endpoint is required in client config")
|
||||
}
|
||||
// Use a provided endpoint target so that for https:// without any tls config given, then
|
||||
// grpc will assume the certificate server name is the endpoint host.
|
||||
conn, err := client.dialWithBalancer()
|
||||
if err != nil {
|
||||
client.cancel()
|
||||
client.resolver.Close()
|
||||
// TODO: Error like `fmt.Errorf(dialing [%s] failed: %v, strings.Join(cfg.Endpoints, ";"), err)` would help with debugging a lot.
|
||||
return nil, err
|
||||
}
|
||||
client.conn = conn
|
||||
|
||||
client.Cluster = NewCluster(client)
|
||||
client.KV = NewKV(client)
|
||||
client.Lease = NewLease(client)
|
||||
client.Watcher = NewWatcher(client)
|
||||
client.Auth = NewAuth(client)
|
||||
client.Maintenance = NewMaintenance(client)
|
||||
|
||||
//get token with established connection
|
||||
ctx, cancel = client.ctx, func() {}
|
||||
if client.cfg.DialTimeout > 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, client.cfg.DialTimeout)
|
||||
}
|
||||
err = client.getToken(ctx)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
cancel()
|
||||
//TODO: Consider fmt.Errorf("communicating with [%s] failed: %v", strings.Join(cfg.Endpoints, ";"), err)
|
||||
return nil, err
|
||||
}
|
||||
cancel()
|
||||
|
||||
if cfg.RejectOldCluster {
|
||||
if err := client.checkVersion(); err != nil {
|
||||
client.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
go client.autoSync()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// roundRobinQuorumBackoff retries against quorum between each backoff.
|
||||
// This is intended for use with a round robin load balancer.
|
||||
func (c *Client) roundRobinQuorumBackoff(waitBetween time.Duration, jitterFraction float64) backoffFunc {
|
||||
return func(attempt uint) time.Duration {
|
||||
// after each round robin across quorum, backoff for our wait between duration
|
||||
n := uint(len(c.Endpoints()))
|
||||
quorum := (n/2 + 1)
|
||||
if attempt%quorum == 0 {
|
||||
c.lg.Debug("backoff", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum), zap.Duration("waitBetween", waitBetween), zap.Float64("jitterFraction", jitterFraction))
|
||||
return jitterUp(waitBetween, jitterFraction)
|
||||
}
|
||||
c.lg.Debug("backoff skipped", zap.Uint("attempt", attempt), zap.Uint("quorum", quorum))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) checkVersion() (err error) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
eps := c.Endpoints()
|
||||
errc := make(chan error, len(eps))
|
||||
ctx, cancel := context.WithCancel(c.ctx)
|
||||
if c.cfg.DialTimeout > 0 {
|
||||
cancel()
|
||||
ctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
|
||||
}
|
||||
|
||||
wg.Add(len(eps))
|
||||
for _, ep := range eps {
|
||||
// if cluster is current, any endpoint gives a recent version
|
||||
go func(e string) {
|
||||
defer wg.Done()
|
||||
resp, rerr := c.Status(ctx, e)
|
||||
if rerr != nil {
|
||||
errc <- rerr
|
||||
return
|
||||
}
|
||||
vs := strings.Split(resp.Version, ".")
|
||||
maj, min := 0, 0
|
||||
if len(vs) >= 2 {
|
||||
var serr error
|
||||
if maj, serr = strconv.Atoi(vs[0]); serr != nil {
|
||||
errc <- serr
|
||||
return
|
||||
}
|
||||
if min, serr = strconv.Atoi(vs[1]); serr != nil {
|
||||
errc <- serr
|
||||
return
|
||||
}
|
||||
}
|
||||
if maj < 3 || (maj == 3 && min < 2) {
|
||||
rerr = ErrOldCluster
|
||||
}
|
||||
errc <- rerr
|
||||
}(ep)
|
||||
}
|
||||
// wait for success
|
||||
for range eps {
|
||||
if err = <-errc; err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
wg.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
// ActiveConnection returns the current in-use connection
|
||||
func (c *Client) ActiveConnection() *grpc.ClientConn { return c.conn }
|
||||
|
||||
// isHaltErr returns true if the given error and context indicate no forward
|
||||
// progress can be made, even after reconnecting.
|
||||
func isHaltErr(ctx context.Context, err error) bool {
|
||||
if ctx != nil && ctx.Err() != nil {
|
||||
return true
|
||||
}
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ev, _ := status.FromError(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 ev.Code() != codes.Unavailable && ev.Code() != codes.Internal
|
||||
}
|
||||
|
||||
// isUnavailableErr returns true if the given error is an unavailable error
|
||||
func isUnavailableErr(ctx context.Context, err error) bool {
|
||||
if ctx != nil && ctx.Err() != nil {
|
||||
return false
|
||||
}
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
ev, ok := status.FromError(err)
|
||||
if ok {
|
||||
// Unavailable codes mean the system will be right back.
|
||||
// (e.g., can't connect, lost leader)
|
||||
return ev.Code() == codes.Unavailable
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toErr(ctx context.Context, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = rpctypes.Error(err)
|
||||
if _, ok := err.(rpctypes.EtcdError); ok {
|
||||
return err
|
||||
}
|
||||
if ev, ok := status.FromError(err); ok {
|
||||
code := ev.Code()
|
||||
switch code {
|
||||
case codes.DeadlineExceeded:
|
||||
fallthrough
|
||||
case codes.Canceled:
|
||||
if ctx.Err() != nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func canceledByCaller(stopCtx context.Context, err error) bool {
|
||||
if stopCtx.Err() == nil || err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return err == context.Canceled || err == context.DeadlineExceeded
|
||||
}
|
||||
|
||||
// IsConnCanceled returns true, if error is from a closed gRPC connection.
|
||||
// ref. https://github.com/grpc/grpc-go/pull/1854
|
||||
func IsConnCanceled(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// >= gRPC v1.23.x
|
||||
s, ok := status.FromError(err)
|
||||
if ok {
|
||||
// connection is canceled or server has already closed the connection
|
||||
return s.Code() == codes.Canceled || s.Message() == "transport is closing"
|
||||
}
|
||||
|
||||
// >= gRPC v1.10.x
|
||||
if err == context.Canceled {
|
||||
return true
|
||||
}
|
||||
|
||||
// <= gRPC v1.7.x returns 'errors.New("grpc: the client connection is closing")'
|
||||
return strings.Contains(err.Error(), "grpc: the client connection is closing")
|
||||
}
|
141
vendor/go.etcd.io/etcd/client/v3/cluster.go
generated
vendored
Normal file
141
vendor/go.etcd.io/etcd/client/v3/cluster.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/client/pkg/v3/types"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type (
|
||||
Member pb.Member
|
||||
MemberListResponse pb.MemberListResponse
|
||||
MemberAddResponse pb.MemberAddResponse
|
||||
MemberRemoveResponse pb.MemberRemoveResponse
|
||||
MemberUpdateResponse pb.MemberUpdateResponse
|
||||
MemberPromoteResponse pb.MemberPromoteResponse
|
||||
)
|
||||
|
||||
type Cluster interface {
|
||||
// MemberList lists the current cluster membership.
|
||||
MemberList(ctx context.Context) (*MemberListResponse, error)
|
||||
|
||||
// MemberAdd adds a new member into the cluster.
|
||||
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
|
||||
|
||||
// MemberAddAsLearner adds a new learner member into the cluster.
|
||||
MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
|
||||
|
||||
// MemberRemove removes an existing member from the cluster.
|
||||
MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
|
||||
|
||||
// MemberUpdate updates the peer addresses of the member.
|
||||
MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error)
|
||||
|
||||
// MemberPromote promotes a member from raft learner (non-voting) to raft voting member.
|
||||
MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error)
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
remote pb.ClusterClient
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
func NewCluster(c *Client) Cluster {
|
||||
api := &cluster{remote: RetryClusterClient(c)}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func NewClusterFromClusterClient(remote pb.ClusterClient, c *Client) Cluster {
|
||||
api := &cluster{remote: remote}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||
return c.memberAdd(ctx, peerAddrs, false)
|
||||
}
|
||||
|
||||
func (c *cluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||
return c.memberAdd(ctx, peerAddrs, true)
|
||||
}
|
||||
|
||||
func (c *cluster) memberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) {
|
||||
// fail-fast before panic in rafthttp
|
||||
if _, err := types.NewURLs(peerAddrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &pb.MemberAddRequest{
|
||||
PeerURLs: peerAddrs,
|
||||
IsLearner: isLearner,
|
||||
}
|
||||
resp, err := c.remote.MemberAdd(ctx, r, c.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*MemberAddResponse)(resp), nil
|
||||
}
|
||||
|
||||
func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
|
||||
r := &pb.MemberRemoveRequest{ID: id}
|
||||
resp, err := c.remote.MemberRemove(ctx, r, c.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*MemberRemoveResponse)(resp), nil
|
||||
}
|
||||
|
||||
func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
|
||||
// fail-fast before panic in rafthttp
|
||||
if _, err := types.NewURLs(peerAddrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it is safe to retry on update.
|
||||
r := &pb.MemberUpdateRequest{ID: id, PeerURLs: peerAddrs}
|
||||
resp, err := c.remote.MemberUpdate(ctx, r, c.callOpts...)
|
||||
if err == nil {
|
||||
return (*MemberUpdateResponse)(resp), nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
||||
// it is safe to retry on list.
|
||||
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{Linearizable: true}, c.callOpts...)
|
||||
if err == nil {
|
||||
return (*MemberListResponse)(resp), nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (c *cluster) MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error) {
|
||||
r := &pb.MemberPromoteRequest{ID: id}
|
||||
resp, err := c.remote.MemberPromote(ctx, r, c.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*MemberPromoteResponse)(resp), nil
|
||||
}
|
51
vendor/go.etcd.io/etcd/client/v3/compact_op.go
generated
vendored
Normal file
51
vendor/go.etcd.io/etcd/client/v3/compact_op.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
)
|
||||
|
||||
// CompactOp represents a compact operation.
|
||||
type CompactOp struct {
|
||||
revision int64
|
||||
physical bool
|
||||
}
|
||||
|
||||
// CompactOption configures compact operation.
|
||||
type CompactOption func(*CompactOp)
|
||||
|
||||
func (op *CompactOp) applyCompactOpts(opts []CompactOption) {
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
}
|
||||
|
||||
// OpCompact wraps slice CompactOption to create a CompactOp.
|
||||
func OpCompact(rev int64, opts ...CompactOption) CompactOp {
|
||||
ret := CompactOp{revision: rev}
|
||||
ret.applyCompactOpts(opts)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (op CompactOp) toRequest() *pb.CompactionRequest {
|
||||
return &pb.CompactionRequest{Revision: op.revision, Physical: op.physical}
|
||||
}
|
||||
|
||||
// WithCompactPhysical makes Compact wait until all compacted entries are
|
||||
// removed from the etcd server's storage.
|
||||
func WithCompactPhysical() CompactOption {
|
||||
return func(op *CompactOp) { op.physical = true }
|
||||
}
|
140
vendor/go.etcd.io/etcd/client/v3/compare.go
generated
vendored
Normal file
140
vendor/go.etcd.io/etcd/client/v3/compare.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
)
|
||||
|
||||
type CompareTarget int
|
||||
type CompareResult int
|
||||
|
||||
const (
|
||||
CompareVersion CompareTarget = iota
|
||||
CompareCreated
|
||||
CompareModified
|
||||
CompareValue
|
||||
)
|
||||
|
||||
type Cmp pb.Compare
|
||||
|
||||
func Compare(cmp Cmp, result string, v interface{}) Cmp {
|
||||
var r pb.Compare_CompareResult
|
||||
|
||||
switch result {
|
||||
case "=":
|
||||
r = pb.Compare_EQUAL
|
||||
case "!=":
|
||||
r = pb.Compare_NOT_EQUAL
|
||||
case ">":
|
||||
r = pb.Compare_GREATER
|
||||
case "<":
|
||||
r = pb.Compare_LESS
|
||||
default:
|
||||
panic("Unknown result op")
|
||||
}
|
||||
|
||||
cmp.Result = r
|
||||
switch cmp.Target {
|
||||
case pb.Compare_VALUE:
|
||||
val, ok := v.(string)
|
||||
if !ok {
|
||||
panic("bad compare value")
|
||||
}
|
||||
cmp.TargetUnion = &pb.Compare_Value{Value: []byte(val)}
|
||||
case pb.Compare_VERSION:
|
||||
cmp.TargetUnion = &pb.Compare_Version{Version: mustInt64(v)}
|
||||
case pb.Compare_CREATE:
|
||||
cmp.TargetUnion = &pb.Compare_CreateRevision{CreateRevision: mustInt64(v)}
|
||||
case pb.Compare_MOD:
|
||||
cmp.TargetUnion = &pb.Compare_ModRevision{ModRevision: mustInt64(v)}
|
||||
case pb.Compare_LEASE:
|
||||
cmp.TargetUnion = &pb.Compare_Lease{Lease: mustInt64orLeaseID(v)}
|
||||
default:
|
||||
panic("Unknown compare type")
|
||||
}
|
||||
return cmp
|
||||
}
|
||||
|
||||
func Value(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_VALUE}
|
||||
}
|
||||
|
||||
func Version(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_VERSION}
|
||||
}
|
||||
|
||||
func CreateRevision(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_CREATE}
|
||||
}
|
||||
|
||||
func ModRevision(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_MOD}
|
||||
}
|
||||
|
||||
// LeaseValue compares a key's LeaseID to a value of your choosing. The empty
|
||||
// LeaseID is 0, otherwise known as `NoLease`.
|
||||
func LeaseValue(key string) Cmp {
|
||||
return Cmp{Key: []byte(key), Target: pb.Compare_LEASE}
|
||||
}
|
||||
|
||||
// KeyBytes returns the byte slice holding with the comparison key.
|
||||
func (cmp *Cmp) KeyBytes() []byte { return cmp.Key }
|
||||
|
||||
// WithKeyBytes sets the byte slice for the comparison key.
|
||||
func (cmp *Cmp) WithKeyBytes(key []byte) { cmp.Key = key }
|
||||
|
||||
// ValueBytes returns the byte slice holding the comparison value, if any.
|
||||
func (cmp *Cmp) ValueBytes() []byte {
|
||||
if tu, ok := cmp.TargetUnion.(*pb.Compare_Value); ok {
|
||||
return tu.Value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithValueBytes sets the byte slice for the comparison's value.
|
||||
func (cmp *Cmp) WithValueBytes(v []byte) { cmp.TargetUnion.(*pb.Compare_Value).Value = v }
|
||||
|
||||
// WithRange sets the comparison to scan the range [key, end).
|
||||
func (cmp Cmp) WithRange(end string) Cmp {
|
||||
cmp.RangeEnd = []byte(end)
|
||||
return cmp
|
||||
}
|
||||
|
||||
// WithPrefix sets the comparison to scan all keys prefixed by the key.
|
||||
func (cmp Cmp) WithPrefix() Cmp {
|
||||
cmp.RangeEnd = getPrefix(cmp.Key)
|
||||
return cmp
|
||||
}
|
||||
|
||||
// mustInt64 panics if val isn't an int or int64. It returns an int64 otherwise.
|
||||
func mustInt64(val interface{}) int64 {
|
||||
if v, ok := val.(int64); ok {
|
||||
return v
|
||||
}
|
||||
if v, ok := val.(int); ok {
|
||||
return int64(v)
|
||||
}
|
||||
panic("bad value")
|
||||
}
|
||||
|
||||
// mustInt64orLeaseID panics if val isn't a LeaseID, int or int64. It returns an
|
||||
// int64 otherwise.
|
||||
func mustInt64orLeaseID(val interface{}) int64 {
|
||||
if v, ok := val.(LeaseID); ok {
|
||||
return int64(v)
|
||||
}
|
||||
return mustInt64(val)
|
||||
}
|
92
vendor/go.etcd.io/etcd/client/v3/config.go
generated
vendored
Normal file
92
vendor/go.etcd.io/etcd/client/v3/config.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// Endpoints is a list of URLs.
|
||||
Endpoints []string `json:"endpoints"`
|
||||
|
||||
// AutoSyncInterval is the interval to update endpoints with its latest members.
|
||||
// 0 disables auto-sync. By default auto-sync is disabled.
|
||||
AutoSyncInterval time.Duration `json:"auto-sync-interval"`
|
||||
|
||||
// DialTimeout is the timeout for failing to establish a connection.
|
||||
DialTimeout time.Duration `json:"dial-timeout"`
|
||||
|
||||
// DialKeepAliveTime is the time after which client pings the server to see if
|
||||
// transport is alive.
|
||||
DialKeepAliveTime time.Duration `json:"dial-keep-alive-time"`
|
||||
|
||||
// DialKeepAliveTimeout is the time that the client waits for a response for the
|
||||
// keep-alive probe. If the response is not received in this time, the connection is closed.
|
||||
DialKeepAliveTimeout time.Duration `json:"dial-keep-alive-timeout"`
|
||||
|
||||
// MaxCallSendMsgSize is the client-side request send limit in bytes.
|
||||
// If 0, it defaults to 2.0 MiB (2 * 1024 * 1024).
|
||||
// Make sure that "MaxCallSendMsgSize" < server-side default send/recv limit.
|
||||
// ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes").
|
||||
MaxCallSendMsgSize int
|
||||
|
||||
// MaxCallRecvMsgSize is the client-side response receive limit.
|
||||
// If 0, it defaults to "math.MaxInt32", because range response can
|
||||
// easily exceed request send limits.
|
||||
// Make sure that "MaxCallRecvMsgSize" >= server-side default send/recv limit.
|
||||
// ("--max-request-bytes" flag to etcd or "embed.Config.MaxRequestBytes").
|
||||
MaxCallRecvMsgSize int
|
||||
|
||||
// TLS holds the client secure credentials, if any.
|
||||
TLS *tls.Config
|
||||
|
||||
// Username is a user name for authentication.
|
||||
Username string `json:"username"`
|
||||
|
||||
// Password is a password for authentication.
|
||||
Password string `json:"password"`
|
||||
|
||||
// RejectOldCluster when set will refuse to create a client against an outdated cluster.
|
||||
RejectOldCluster bool `json:"reject-old-cluster"`
|
||||
|
||||
// DialOptions is a list of dial options for the grpc client (e.g., for interceptors).
|
||||
// For example, pass "grpc.WithBlock()" to block until the underlying connection is up.
|
||||
// Without this, Dial returns immediately and connecting the server happens in background.
|
||||
DialOptions []grpc.DialOption
|
||||
|
||||
// Context is the default client context; it can be used to cancel grpc dial out and
|
||||
// other operations that do not have an explicit context.
|
||||
Context context.Context
|
||||
|
||||
// Logger sets client-side logger.
|
||||
// If nil, fallback to building LogConfig.
|
||||
Logger *zap.Logger
|
||||
|
||||
// LogConfig configures client-side logger.
|
||||
// If nil, use the default logger.
|
||||
// TODO: configure gRPC logger
|
||||
LogConfig *zap.Config
|
||||
|
||||
// PermitWithoutStream when set will allow client to send keepalive pings to server without any active streams(RPCs).
|
||||
PermitWithoutStream bool `json:"permit-without-stream"`
|
||||
|
||||
// TODO: support custom balancer picker
|
||||
}
|
131
vendor/go.etcd.io/etcd/client/v3/credentials/credentials.go
generated
vendored
Normal file
131
vendor/go.etcd.io/etcd/client/v3/credentials/credentials.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2019 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package credentials implements gRPC credential interface with etcd specific logic.
|
||||
// e.g., client handshake with custom authority parameter
|
||||
package credentials
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
grpccredentials "google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
// Config defines gRPC credential configuration.
|
||||
type Config struct {
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// Bundle defines gRPC credential interface.
|
||||
type Bundle interface {
|
||||
grpccredentials.Bundle
|
||||
UpdateAuthToken(token string)
|
||||
}
|
||||
|
||||
// NewBundle constructs a new gRPC credential bundle.
|
||||
func NewBundle(cfg Config) Bundle {
|
||||
return &bundle{
|
||||
tc: newTransportCredential(cfg.TLSConfig),
|
||||
rc: newPerRPCCredential(),
|
||||
}
|
||||
}
|
||||
|
||||
// bundle implements "grpccredentials.Bundle" interface.
|
||||
type bundle struct {
|
||||
tc *transportCredential
|
||||
rc *perRPCCredential
|
||||
}
|
||||
|
||||
func (b *bundle) TransportCredentials() grpccredentials.TransportCredentials {
|
||||
return b.tc
|
||||
}
|
||||
|
||||
func (b *bundle) PerRPCCredentials() grpccredentials.PerRPCCredentials {
|
||||
return b.rc
|
||||
}
|
||||
|
||||
func (b *bundle) NewWithMode(mode string) (grpccredentials.Bundle, error) {
|
||||
// no-op
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// transportCredential implements "grpccredentials.TransportCredentials" interface.
|
||||
type transportCredential struct {
|
||||
gtc grpccredentials.TransportCredentials
|
||||
}
|
||||
|
||||
func newTransportCredential(cfg *tls.Config) *transportCredential {
|
||||
return &transportCredential{
|
||||
gtc: grpccredentials.NewTLS(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *transportCredential) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) {
|
||||
return tc.gtc.ClientHandshake(ctx, authority, rawConn)
|
||||
}
|
||||
|
||||
func (tc *transportCredential) ServerHandshake(rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) {
|
||||
return tc.gtc.ServerHandshake(rawConn)
|
||||
}
|
||||
|
||||
func (tc *transportCredential) Info() grpccredentials.ProtocolInfo {
|
||||
return tc.gtc.Info()
|
||||
}
|
||||
|
||||
func (tc *transportCredential) Clone() grpccredentials.TransportCredentials {
|
||||
return &transportCredential{
|
||||
gtc: tc.gtc.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *transportCredential) OverrideServerName(serverNameOverride string) error {
|
||||
return tc.gtc.OverrideServerName(serverNameOverride)
|
||||
}
|
||||
|
||||
// perRPCCredential implements "grpccredentials.PerRPCCredentials" interface.
|
||||
type perRPCCredential struct {
|
||||
authToken string
|
||||
authTokenMu sync.RWMutex
|
||||
}
|
||||
|
||||
func newPerRPCCredential() *perRPCCredential { return &perRPCCredential{} }
|
||||
|
||||
func (rc *perRPCCredential) RequireTransportSecurity() bool { return false }
|
||||
|
||||
func (rc *perRPCCredential) GetRequestMetadata(ctx context.Context, s ...string) (map[string]string, error) {
|
||||
rc.authTokenMu.RLock()
|
||||
authToken := rc.authToken
|
||||
rc.authTokenMu.RUnlock()
|
||||
if authToken == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return map[string]string{rpctypes.TokenFieldNameGRPC: authToken}, nil
|
||||
}
|
||||
|
||||
func (b *bundle) UpdateAuthToken(token string) {
|
||||
if b.rc == nil {
|
||||
return
|
||||
}
|
||||
b.rc.UpdateAuthToken(token)
|
||||
}
|
||||
|
||||
func (rc *perRPCCredential) UpdateAuthToken(token string) {
|
||||
rc.authTokenMu.Lock()
|
||||
rc.authToken = token
|
||||
rc.authTokenMu.Unlock()
|
||||
}
|
50
vendor/go.etcd.io/etcd/client/v3/ctx.go
generated
vendored
Normal file
50
vendor/go.etcd.io/etcd/client/v3/ctx.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2020 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
"go.etcd.io/etcd/api/v3/version"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// WithRequireLeader requires client requests to only succeed
|
||||
// when the cluster has a leader.
|
||||
func WithRequireLeader(ctx context.Context) context.Context {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok { // no outgoing metadata ctx key, create one
|
||||
md = metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
copied := md.Copy() // avoid racey updates
|
||||
// overwrite/add 'hasleader' key/value
|
||||
copied.Set(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||
return metadata.NewOutgoingContext(ctx, copied)
|
||||
}
|
||||
|
||||
// embeds client version
|
||||
func withVersion(ctx context.Context) context.Context {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok { // no outgoing metadata ctx key, create one
|
||||
md = metadata.Pairs(rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
|
||||
return metadata.NewOutgoingContext(ctx, md)
|
||||
}
|
||||
copied := md.Copy() // avoid racey updates
|
||||
// overwrite/add version key/value
|
||||
copied.Set(rpctypes.MetadataClientAPIVersionKey, version.APIVersion)
|
||||
return metadata.NewOutgoingContext(ctx, copied)
|
||||
}
|
106
vendor/go.etcd.io/etcd/client/v3/doc.go
generated
vendored
Normal file
106
vendor/go.etcd.io/etcd/client/v3/doc.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package clientv3 implements the official Go etcd client for v3.
|
||||
//
|
||||
// Create client using `clientv3.New`:
|
||||
//
|
||||
// // expect dial time-out on ipv4 blackhole
|
||||
// _, err := clientv3.New(clientv3.Config{
|
||||
// Endpoints: []string{"http://254.0.0.1:12345"},
|
||||
// DialTimeout: 2 * time.Second,
|
||||
// })
|
||||
//
|
||||
// // etcd clientv3 >= v3.2.10, grpc/grpc-go >= v1.7.3
|
||||
// if err == context.DeadlineExceeded {
|
||||
// // handle errors
|
||||
// }
|
||||
//
|
||||
// // etcd clientv3 <= v3.2.9, grpc/grpc-go <= v1.2.1
|
||||
// if err == grpc.ErrClientConnTimeout {
|
||||
// // handle errors
|
||||
// }
|
||||
//
|
||||
// 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()
|
||||
//
|
||||
// Make sure to close the client after using it. If the client is not closed, the
|
||||
// connection will have leaky goroutines.
|
||||
//
|
||||
// To specify a client request timeout, wrap the context with context.WithTimeout:
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// The Client has internal state (watchers and leases), so Clients should be reused instead of created as needed.
|
||||
// Clients are safe for concurrent use by multiple goroutines.
|
||||
//
|
||||
// etcd client returns 2 types of errors:
|
||||
//
|
||||
// 1. context error: canceled or deadline exceeded.
|
||||
// 2. gRPC error: e.g. when clock drifts in server-side before client's context deadline exceeded.
|
||||
// See https://github.com/etcd-io/etcd/blob/main/api/v3rpc/rpctypes/error.go
|
||||
//
|
||||
// Here is the example code to handle client errors:
|
||||
//
|
||||
// resp, err := kvc.Put(ctx, "", "")
|
||||
// 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 err == rpctypes.ErrEmptyKey {
|
||||
// // client-side error: key is not provided
|
||||
// } else if ev, ok := status.FromError(err); ok {
|
||||
// code := ev.Code()
|
||||
// if code == codes.DeadlineExceeded {
|
||||
// // server-side context might have timed-out first (due to clock skew)
|
||||
// // while original client-side context is not timed-out yet
|
||||
// }
|
||||
// } else {
|
||||
// // bad cluster endpoints, which are not etcd servers
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// go func() { cli.Close() }()
|
||||
// _, err := kvc.Get(ctx, "a")
|
||||
// if err != nil {
|
||||
// // with etcd clientv3 <= v3.3
|
||||
// if err == context.Canceled {
|
||||
// // grpc balancer calls 'Get' with an inflight client.Close
|
||||
// } else if err == grpc.ErrClientConnClosing { // <= gRCP v1.7.x
|
||||
// // grpc balancer calls 'Get' after client.Close.
|
||||
// }
|
||||
// // with etcd clientv3 >= v3.4
|
||||
// if clientv3.IsConnCanceled(err) {
|
||||
// // gRPC client connection is closed
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The grpc load balancer is registered statically and is shared across etcd clients.
|
||||
// To enable detailed load balancer logging, set the ETCD_CLIENT_DEBUG environment
|
||||
// variable. E.g. "ETCD_CLIENT_DEBUG=1".
|
||||
//
|
||||
package clientv3
|
137
vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go
generated
vendored
Normal file
137
vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2021 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CredsRequirement int
|
||||
|
||||
const (
|
||||
// CREDS_REQUIRE - Credentials/certificate required for thi type of connection.
|
||||
CREDS_REQUIRE CredsRequirement = iota
|
||||
// CREDS_DROP - Credentials/certificate not needed and should get ignored.
|
||||
CREDS_DROP
|
||||
// CREDS_OPTIONAL - Credentials/certificate might be used if supplied
|
||||
CREDS_OPTIONAL
|
||||
)
|
||||
|
||||
func extractHostFromHostPort(ep string) string {
|
||||
host, _, err := net.SplitHostPort(ep)
|
||||
if err != nil {
|
||||
return ep
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func extractHostFromPath(pathStr string) string {
|
||||
return extractHostFromHostPort(path.Base(pathStr))
|
||||
}
|
||||
|
||||
//mustSplit2 returns the values from strings.SplitN(s, sep, 2).
|
||||
//If sep is not found, it returns ("", "", false) instead.
|
||||
func mustSplit2(s, sep string) (string, string) {
|
||||
spl := strings.SplitN(s, sep, 2)
|
||||
if len(spl) < 2 {
|
||||
panic(fmt.Errorf("token '%v' expected to have separator sep: `%v`", s, sep))
|
||||
}
|
||||
return spl[0], spl[1]
|
||||
}
|
||||
|
||||
func schemeToCredsRequirement(schema string) CredsRequirement {
|
||||
switch schema {
|
||||
case "https", "unixs":
|
||||
return CREDS_REQUIRE
|
||||
case "http":
|
||||
return CREDS_DROP
|
||||
case "unix":
|
||||
// Preserving previous behavior from:
|
||||
// https://github.com/etcd-io/etcd/blob/dae29bb719dd69dc119146fc297a0628fcc1ccf8/client/v3/client.go#L212
|
||||
// that likely was a bug due to missing 'fallthrough'.
|
||||
// At the same time it seems legit to let the users decide whether they
|
||||
// want credential control or not (and 'unixs' schema is not a standard thing).
|
||||
return CREDS_OPTIONAL
|
||||
case "":
|
||||
return CREDS_OPTIONAL
|
||||
default:
|
||||
return CREDS_OPTIONAL
|
||||
}
|
||||
}
|
||||
|
||||
// This function translates endpoints names supported by etcd server into
|
||||
// endpoints as supported by grpc with additional information
|
||||
// (server_name for cert validation, requireCreds - whether certs are needed).
|
||||
// The main differences:
|
||||
// - etcd supports unixs & https names as opposed to unix & http to
|
||||
// distinguish need to configure certificates.
|
||||
// - etcd support http(s) names as opposed to tcp supported by grpc/dial method.
|
||||
// - etcd supports unix(s)://local-file naming schema
|
||||
// (as opposed to unix:local-file canonical name used by grpc for current dir files).
|
||||
// - Within the unix(s) schemas, the last segment (filename) without 'port' (content after colon)
|
||||
// is considered serverName - to allow local testing of cert-protected communication.
|
||||
// See more:
|
||||
// - https://github.com/grpc/grpc-go/blob/26c143bd5f59344a4b8a1e491e0f5e18aa97abc7/internal/grpcutil/target.go#L47
|
||||
// - https://golang.org/pkg/net/#Dial
|
||||
// - https://github.com/grpc/grpc/blob/master/doc/naming.md
|
||||
func translateEndpoint(ep string) (addr string, serverName string, requireCreds CredsRequirement) {
|
||||
if strings.HasPrefix(ep, "unix:") || strings.HasPrefix(ep, "unixs:") {
|
||||
if strings.HasPrefix(ep, "unix:///") || strings.HasPrefix(ep, "unixs:///") {
|
||||
// absolute path case
|
||||
schema, absolutePath := mustSplit2(ep, "://")
|
||||
return "unix://" + absolutePath, extractHostFromPath(absolutePath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") {
|
||||
// legacy etcd local path
|
||||
schema, localPath := mustSplit2(ep, "://")
|
||||
return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
schema, localPath := mustSplit2(ep, ":")
|
||||
return "unix:" + localPath, extractHostFromPath(localPath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
|
||||
if strings.Contains(ep, "://") {
|
||||
url, err := url.Parse(ep)
|
||||
if err != nil {
|
||||
return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL
|
||||
}
|
||||
if url.Scheme == "http" || url.Scheme == "https" {
|
||||
return url.Host, url.Hostname(), schemeToCredsRequirement(url.Scheme)
|
||||
}
|
||||
return ep, url.Hostname(), schemeToCredsRequirement(url.Scheme)
|
||||
}
|
||||
// Handles plain addresses like 10.0.0.44:437.
|
||||
return ep, extractHostFromHostPort(ep), CREDS_OPTIONAL
|
||||
}
|
||||
|
||||
// RequiresCredentials returns whether given endpoint requires
|
||||
// credentials/certificates for connection.
|
||||
func RequiresCredentials(ep string) CredsRequirement {
|
||||
_, _, requireCreds := translateEndpoint(ep)
|
||||
return requireCreds
|
||||
}
|
||||
|
||||
// Interpret endpoint parses an endpoint of the form
|
||||
// (http|https)://<host>*|(unix|unixs)://<path>)
|
||||
// and returns low-level address (supported by 'net') to connect to,
|
||||
// and a server name used for x509 certificate matching.
|
||||
func Interpret(ep string) (address string, serverName string) {
|
||||
addr, serverName, _ := translateEndpoint(ep)
|
||||
return addr, serverName
|
||||
}
|
74
vendor/go.etcd.io/etcd/client/v3/internal/resolver/resolver.go
generated
vendored
Normal file
74
vendor/go.etcd.io/etcd/client/v3/internal/resolver/resolver.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2021 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"go.etcd.io/etcd/client/v3/internal/endpoint"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
"google.golang.org/grpc/serviceconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
Schema = "etcd-endpoints"
|
||||
)
|
||||
|
||||
// EtcdManualResolver is a Resolver (and resolver.Builder) that can be updated
|
||||
// using SetEndpoints.
|
||||
type EtcdManualResolver struct {
|
||||
*manual.Resolver
|
||||
endpoints []string
|
||||
serviceConfig *serviceconfig.ParseResult
|
||||
}
|
||||
|
||||
func New(endpoints ...string) *EtcdManualResolver {
|
||||
r := manual.NewBuilderWithScheme(Schema)
|
||||
return &EtcdManualResolver{Resolver: r, endpoints: endpoints, serviceConfig: nil}
|
||||
}
|
||||
|
||||
// Build returns itself for Resolver, because it's both a builder and a resolver.
|
||||
func (r *EtcdManualResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||
r.serviceConfig = cc.ParseServiceConfig(`{"loadBalancingPolicy": "round_robin"}`)
|
||||
if r.serviceConfig.Err != nil {
|
||||
return nil, r.serviceConfig.Err
|
||||
}
|
||||
res, err := r.Resolver.Build(target, cc, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Populates endpoints stored in r into ClientConn (cc).
|
||||
r.updateState()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *EtcdManualResolver) SetEndpoints(endpoints []string) {
|
||||
r.endpoints = endpoints
|
||||
r.updateState()
|
||||
}
|
||||
|
||||
func (r EtcdManualResolver) updateState() {
|
||||
if r.CC != nil {
|
||||
addresses := make([]resolver.Address, len(r.endpoints))
|
||||
for i, ep := range r.endpoints {
|
||||
addr, serverName := endpoint.Interpret(ep)
|
||||
addresses[i] = resolver.Address{Addr: addr, ServerName: serverName}
|
||||
}
|
||||
state := resolver.State{
|
||||
Addresses: addresses,
|
||||
ServiceConfig: r.serviceConfig,
|
||||
}
|
||||
r.UpdateState(state)
|
||||
}
|
||||
}
|
177
vendor/go.etcd.io/etcd/client/v3/kv.go
generated
vendored
Normal file
177
vendor/go.etcd.io/etcd/client/v3/kv.go
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
// Copyright 2015 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type (
|
||||
CompactResponse pb.CompactionResponse
|
||||
PutResponse pb.PutResponse
|
||||
GetResponse pb.RangeResponse
|
||||
DeleteResponse pb.DeleteRangeResponse
|
||||
TxnResponse pb.TxnResponse
|
||||
)
|
||||
|
||||
type KV interface {
|
||||
// Put puts a key-value pair into etcd.
|
||||
// Note that key,value can be plain bytes array and string is
|
||||
// an immutable representation of that bytes array.
|
||||
// To get a string of bytes, do string([]byte{0x10, 0x20}).
|
||||
Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)
|
||||
|
||||
// Get retrieves keys.
|
||||
// By default, Get will return the value for "key", if any.
|
||||
// When passed WithRange(end), Get will return the keys in the range [key, end).
|
||||
// When passed WithFromKey(), Get returns keys greater than or equal to key.
|
||||
// When passed WithRev(rev) with rev > 0, Get retrieves keys at the given revision;
|
||||
// if the required revision is compacted, the request will fail with ErrCompacted .
|
||||
// When passed WithLimit(limit), the number of returned keys is bounded by limit.
|
||||
// When passed WithSort(), the keys will be sorted.
|
||||
Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)
|
||||
|
||||
// Delete deletes a key, or optionally using WithRange(end), [key, end).
|
||||
Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)
|
||||
|
||||
// Compact compacts etcd KV history before the given rev.
|
||||
Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)
|
||||
|
||||
// Do applies a single Op on KV without a transaction.
|
||||
// Do is useful when creating arbitrary operations to be issued at a
|
||||
// later time; the user can range over the operations, calling Do to
|
||||
// execute them. Get/Put/Delete, on the other hand, are best suited
|
||||
// for when the operation should be issued at the time of declaration.
|
||||
Do(ctx context.Context, op Op) (OpResponse, error)
|
||||
|
||||
// Txn creates a transaction.
|
||||
Txn(ctx context.Context) Txn
|
||||
}
|
||||
|
||||
type OpResponse struct {
|
||||
put *PutResponse
|
||||
get *GetResponse
|
||||
del *DeleteResponse
|
||||
txn *TxnResponse
|
||||
}
|
||||
|
||||
func (op OpResponse) Put() *PutResponse { return op.put }
|
||||
func (op OpResponse) Get() *GetResponse { return op.get }
|
||||
func (op OpResponse) Del() *DeleteResponse { return op.del }
|
||||
func (op OpResponse) Txn() *TxnResponse { return op.txn }
|
||||
|
||||
func (resp *PutResponse) OpResponse() OpResponse {
|
||||
return OpResponse{put: resp}
|
||||
}
|
||||
func (resp *GetResponse) OpResponse() OpResponse {
|
||||
return OpResponse{get: resp}
|
||||
}
|
||||
func (resp *DeleteResponse) OpResponse() OpResponse {
|
||||
return OpResponse{del: resp}
|
||||
}
|
||||
func (resp *TxnResponse) OpResponse() OpResponse {
|
||||
return OpResponse{txn: resp}
|
||||
}
|
||||
|
||||
type kv struct {
|
||||
remote pb.KVClient
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
func NewKV(c *Client) KV {
|
||||
api := &kv{remote: RetryKVClient(c)}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func NewKVFromKVClient(remote pb.KVClient, c *Client) KV {
|
||||
api := &kv{remote: remote}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) {
|
||||
r, err := kv.Do(ctx, OpGet(key, opts...))
|
||||
return r.get, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
|
||||
r, err := kv.Do(ctx, OpDelete(key, opts...))
|
||||
return r.del, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
|
||||
resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest(), kv.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*CompactResponse)(resp), err
|
||||
}
|
||||
|
||||
func (kv *kv) Txn(ctx context.Context) Txn {
|
||||
return &txn{
|
||||
kv: kv,
|
||||
ctx: ctx,
|
||||
callOpts: kv.callOpts,
|
||||
}
|
||||
}
|
||||
|
||||
func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) {
|
||||
var err error
|
||||
switch op.t {
|
||||
case tRange:
|
||||
var resp *pb.RangeResponse
|
||||
resp, err = kv.remote.Range(ctx, op.toRangeRequest(), kv.callOpts...)
|
||||
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), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
|
||||
resp, err = kv.remote.Put(ctx, r, kv.callOpts...)
|
||||
if err == nil {
|
||||
return OpResponse{put: (*PutResponse)(resp)}, nil
|
||||
}
|
||||
case tDeleteRange:
|
||||
var resp *pb.DeleteRangeResponse
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
|
||||
resp, err = kv.remote.DeleteRange(ctx, r, kv.callOpts...)
|
||||
if err == nil {
|
||||
return OpResponse{del: (*DeleteResponse)(resp)}, nil
|
||||
}
|
||||
case tTxn:
|
||||
var resp *pb.TxnResponse
|
||||
resp, err = kv.remote.Txn(ctx, op.toTxnRequest(), kv.callOpts...)
|
||||
if err == nil {
|
||||
return OpResponse{txn: (*TxnResponse)(resp)}, nil
|
||||
}
|
||||
default:
|
||||
panic("Unknown op")
|
||||
}
|
||||
return OpResponse{}, toErr(ctx, err)
|
||||
}
|
605
vendor/go.etcd.io/etcd/client/v3/lease.go
generated
vendored
Normal file
605
vendor/go.etcd.io/etcd/client/v3/lease.go
generated
vendored
Normal file
@ -0,0 +1,605 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type (
|
||||
LeaseRevokeResponse pb.LeaseRevokeResponse
|
||||
LeaseID int64
|
||||
)
|
||||
|
||||
// LeaseGrantResponse wraps the protobuf message LeaseGrantResponse.
|
||||
type LeaseGrantResponse struct {
|
||||
*pb.ResponseHeader
|
||||
ID LeaseID
|
||||
TTL int64
|
||||
Error string
|
||||
}
|
||||
|
||||
// LeaseKeepAliveResponse wraps the protobuf message LeaseKeepAliveResponse.
|
||||
type LeaseKeepAliveResponse struct {
|
||||
*pb.ResponseHeader
|
||||
ID LeaseID
|
||||
TTL int64
|
||||
}
|
||||
|
||||
// LeaseTimeToLiveResponse wraps the protobuf message LeaseTimeToLiveResponse.
|
||||
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. Expired lease will return -1.
|
||||
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"`
|
||||
}
|
||||
|
||||
// LeaseStatus represents a lease status.
|
||||
type LeaseStatus struct {
|
||||
ID LeaseID `json:"id"`
|
||||
// TODO: TTL int64
|
||||
}
|
||||
|
||||
// LeaseLeasesResponse wraps the protobuf message LeaseLeasesResponse.
|
||||
type LeaseLeasesResponse struct {
|
||||
*pb.ResponseHeader
|
||||
Leases []LeaseStatus `json:"leases"`
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultTTL is the assumed lease TTL used for the first keepalive
|
||||
// deadline before the actual TTL is known to the client.
|
||||
defaultTTL = 5 * time.Second
|
||||
// NoLease is a lease ID for the absence of a lease.
|
||||
NoLease LeaseID = 0
|
||||
|
||||
// retryConnWait is how long to wait before retrying request due to an error
|
||||
retryConnWait = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
// LeaseResponseChSize is the size of buffer to store unsent lease responses.
|
||||
// WARNING: DO NOT UPDATE.
|
||||
// Only for testing purposes.
|
||||
var LeaseResponseChSize = 16
|
||||
|
||||
// ErrKeepAliveHalted is returned if client keep alive loop halts with an unexpected error.
|
||||
//
|
||||
// This usually means that automatic lease renewal via KeepAlive is broken, but KeepAliveOnce will still work as expected.
|
||||
type ErrKeepAliveHalted struct {
|
||||
Reason error
|
||||
}
|
||||
|
||||
func (e ErrKeepAliveHalted) Error() string {
|
||||
s := "etcdclient: leases keep alive halted"
|
||||
if e.Reason != nil {
|
||||
s += ": " + e.Reason.Error()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type Lease interface {
|
||||
// Grant creates a new lease.
|
||||
Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error)
|
||||
|
||||
// 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)
|
||||
|
||||
// Leases retrieves all leases.
|
||||
Leases(ctx context.Context) (*LeaseLeasesResponse, error)
|
||||
|
||||
// KeepAlive attempts to keep the given lease alive forever. If the keepalive responses posted
|
||||
// to the channel are not consumed promptly the channel may become full. When full, the lease
|
||||
// client will continue sending keep alive requests to the etcd server, but will drop responses
|
||||
// until there is capacity on the channel to send more responses.
|
||||
//
|
||||
// If client keep alive loop halts with an unexpected error (e.g. "etcdserver: no leader") or
|
||||
// canceled by the caller (e.g. context.Canceled), KeepAlive returns a ErrKeepAliveHalted error
|
||||
// containing the error reason.
|
||||
//
|
||||
// The returned "LeaseKeepAliveResponse" channel closes if underlying keep
|
||||
// alive stream is interrupted in some way the client cannot handle itself;
|
||||
// given context "ctx" is canceled or timed out.
|
||||
//
|
||||
// TODO(v4.0): post errors to last keep alive message before closing
|
||||
// (see https://github.com/etcd-io/etcd/pull/7866)
|
||||
KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)
|
||||
|
||||
// KeepAliveOnce renews the lease once. The response corresponds to the
|
||||
// first message from calling KeepAlive. If the response has a recoverable
|
||||
// error, KeepAliveOnce will retry the RPC with a new keep alive message.
|
||||
//
|
||||
// In most of the cases, Keepalive should be used instead of KeepAliveOnce.
|
||||
KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error)
|
||||
|
||||
// Close releases all resources Lease keeps for efficient communication
|
||||
// with the etcd server.
|
||||
Close() error
|
||||
}
|
||||
|
||||
type lessor struct {
|
||||
mu sync.Mutex // guards all fields
|
||||
|
||||
// donec is closed and loopErr is set when recvKeepAliveLoop stops
|
||||
donec chan struct{}
|
||||
loopErr error
|
||||
|
||||
remote pb.LeaseClient
|
||||
|
||||
stream pb.Lease_LeaseKeepAliveClient
|
||||
streamCancel context.CancelFunc
|
||||
|
||||
stopCtx context.Context
|
||||
stopCancel context.CancelFunc
|
||||
|
||||
keepAlives map[LeaseID]*keepAlive
|
||||
|
||||
// firstKeepAliveTimeout is the timeout for the first keepalive request
|
||||
// before the actual TTL is known to the lease client
|
||||
firstKeepAliveTimeout time.Duration
|
||||
|
||||
// firstKeepAliveOnce ensures stream starts after first KeepAlive call.
|
||||
firstKeepAliveOnce sync.Once
|
||||
|
||||
callOpts []grpc.CallOption
|
||||
|
||||
lg *zap.Logger
|
||||
}
|
||||
|
||||
// keepAlive multiplexes a keepalive for a lease over multiple channels
|
||||
type keepAlive struct {
|
||||
chs []chan<- *LeaseKeepAliveResponse
|
||||
ctxs []context.Context
|
||||
// deadline is the time the keep alive channels close if no response
|
||||
deadline time.Time
|
||||
// nextKeepAlive is when to send the next keep alive message
|
||||
nextKeepAlive time.Time
|
||||
// donec is closed on lease revoke, expiration, or cancel.
|
||||
donec chan struct{}
|
||||
}
|
||||
|
||||
func NewLease(c *Client) Lease {
|
||||
return NewLeaseFromLeaseClient(RetryLeaseClient(c), c, c.cfg.DialTimeout+time.Second)
|
||||
}
|
||||
|
||||
func NewLeaseFromLeaseClient(remote pb.LeaseClient, c *Client, keepAliveTimeout time.Duration) Lease {
|
||||
l := &lessor{
|
||||
donec: make(chan struct{}),
|
||||
keepAlives: make(map[LeaseID]*keepAlive),
|
||||
remote: remote,
|
||||
firstKeepAliveTimeout: keepAliveTimeout,
|
||||
lg: c.lg,
|
||||
}
|
||||
if l.firstKeepAliveTimeout == time.Second {
|
||||
l.firstKeepAliveTimeout = defaultTTL
|
||||
}
|
||||
if c != nil {
|
||||
l.callOpts = c.callOpts
|
||||
}
|
||||
reqLeaderCtx := WithRequireLeader(context.Background())
|
||||
l.stopCtx, l.stopCancel = context.WithCancel(reqLeaderCtx)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *lessor) Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error) {
|
||||
r := &pb.LeaseGrantRequest{TTL: ttl}
|
||||
resp, err := l.remote.LeaseGrant(ctx, r, l.callOpts...)
|
||||
if err == nil {
|
||||
gresp := &LeaseGrantResponse{
|
||||
ResponseHeader: resp.GetHeader(),
|
||||
ID: LeaseID(resp.ID),
|
||||
TTL: resp.TTL,
|
||||
Error: resp.Error,
|
||||
}
|
||||
return gresp, nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (l *lessor) Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error) {
|
||||
r := &pb.LeaseRevokeRequest{ID: int64(id)}
|
||||
resp, err := l.remote.LeaseRevoke(ctx, r, l.callOpts...)
|
||||
if err == nil {
|
||||
return (*LeaseRevokeResponse)(resp), nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (l *lessor) TimeToLive(ctx context.Context, id LeaseID, opts ...LeaseOption) (*LeaseTimeToLiveResponse, error) {
|
||||
r := toLeaseTimeToLiveRequest(id, opts...)
|
||||
resp, err := l.remote.LeaseTimeToLive(ctx, r, l.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
gresp := &LeaseTimeToLiveResponse{
|
||||
ResponseHeader: resp.GetHeader(),
|
||||
ID: LeaseID(resp.ID),
|
||||
TTL: resp.TTL,
|
||||
GrantedTTL: resp.GrantedTTL,
|
||||
Keys: resp.Keys,
|
||||
}
|
||||
return gresp, nil
|
||||
}
|
||||
|
||||
func (l *lessor) Leases(ctx context.Context) (*LeaseLeasesResponse, error) {
|
||||
resp, err := l.remote.LeaseLeases(ctx, &pb.LeaseLeasesRequest{}, l.callOpts...)
|
||||
if err == nil {
|
||||
leases := make([]LeaseStatus, len(resp.Leases))
|
||||
for i := range resp.Leases {
|
||||
leases[i] = LeaseStatus{ID: LeaseID(resp.Leases[i].ID)}
|
||||
}
|
||||
return &LeaseLeasesResponse{ResponseHeader: resp.GetHeader(), Leases: leases}, nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error) {
|
||||
ch := make(chan *LeaseKeepAliveResponse, LeaseResponseChSize)
|
||||
|
||||
l.mu.Lock()
|
||||
// ensure that recvKeepAliveLoop is still running
|
||||
select {
|
||||
case <-l.donec:
|
||||
err := l.loopErr
|
||||
l.mu.Unlock()
|
||||
close(ch)
|
||||
return ch, ErrKeepAliveHalted{Reason: err}
|
||||
default:
|
||||
}
|
||||
ka, ok := l.keepAlives[id]
|
||||
if !ok {
|
||||
// create fresh keep alive
|
||||
ka = &keepAlive{
|
||||
chs: []chan<- *LeaseKeepAliveResponse{ch},
|
||||
ctxs: []context.Context{ctx},
|
||||
deadline: time.Now().Add(l.firstKeepAliveTimeout),
|
||||
nextKeepAlive: time.Now(),
|
||||
donec: make(chan struct{}),
|
||||
}
|
||||
l.keepAlives[id] = ka
|
||||
} else {
|
||||
// add channel and context to existing keep alive
|
||||
ka.ctxs = append(ka.ctxs, ctx)
|
||||
ka.chs = append(ka.chs, ch)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
|
||||
go l.keepAliveCtxCloser(ctx, id, ka.donec)
|
||||
l.firstKeepAliveOnce.Do(func() {
|
||||
go l.recvKeepAliveLoop()
|
||||
go l.deadlineLoop()
|
||||
})
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (l *lessor) KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error) {
|
||||
for {
|
||||
resp, err := l.keepAliveOnce(ctx, id)
|
||||
if err == nil {
|
||||
if resp.TTL <= 0 {
|
||||
err = rpctypes.ErrLeaseNotFound
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
if isHaltErr(ctx, err) {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lessor) Close() error {
|
||||
l.stopCancel()
|
||||
// close for synchronous teardown if stream goroutines never launched
|
||||
l.firstKeepAliveOnce.Do(func() { close(l.donec) })
|
||||
<-l.donec
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *lessor) keepAliveCtxCloser(ctx context.Context, id LeaseID, donec <-chan struct{}) {
|
||||
select {
|
||||
case <-donec:
|
||||
return
|
||||
case <-l.donec:
|
||||
return
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
ka, ok := l.keepAlives[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// close channel and remove context if still associated with keep alive
|
||||
for i, c := range ka.ctxs {
|
||||
if c == ctx {
|
||||
close(ka.chs[i])
|
||||
ka.ctxs = append(ka.ctxs[:i], ka.ctxs[i+1:]...)
|
||||
ka.chs = append(ka.chs[:i], ka.chs[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
// remove if no one more listeners
|
||||
if len(ka.chs) == 0 {
|
||||
delete(l.keepAlives, id)
|
||||
}
|
||||
}
|
||||
|
||||
// closeRequireLeader scans keepAlives for ctxs that have require leader
|
||||
// and closes the associated channels.
|
||||
func (l *lessor) closeRequireLeader() {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
for _, ka := range l.keepAlives {
|
||||
reqIdxs := 0
|
||||
// find all required leader channels, close, mark as nil
|
||||
for i, ctx := range ka.ctxs {
|
||||
md, ok := metadata.FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ks := md[rpctypes.MetadataRequireLeaderKey]
|
||||
if len(ks) < 1 || ks[0] != rpctypes.MetadataHasLeader {
|
||||
continue
|
||||
}
|
||||
close(ka.chs[i])
|
||||
ka.chs[i] = nil
|
||||
reqIdxs++
|
||||
}
|
||||
if reqIdxs == 0 {
|
||||
continue
|
||||
}
|
||||
// remove all channels that required a leader from keepalive
|
||||
newChs := make([]chan<- *LeaseKeepAliveResponse, len(ka.chs)-reqIdxs)
|
||||
newCtxs := make([]context.Context, len(newChs))
|
||||
newIdx := 0
|
||||
for i := range ka.chs {
|
||||
if ka.chs[i] == nil {
|
||||
continue
|
||||
}
|
||||
newChs[newIdx], newCtxs[newIdx] = ka.chs[i], ka.ctxs[newIdx]
|
||||
newIdx++
|
||||
}
|
||||
ka.chs, ka.ctxs = newChs, newCtxs
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lessor) keepAliveOnce(ctx context.Context, id LeaseID) (karesp *LeaseKeepAliveResponse, ferr error) {
|
||||
cctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
stream, err := l.remote.LeaseKeepAlive(cctx, l.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
if ferr == nil {
|
||||
ferr = toErr(ctx, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
err = stream.Send(&pb.LeaseKeepAliveRequest{ID: int64(id)})
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
resp, rerr := stream.Recv()
|
||||
if rerr != nil {
|
||||
return nil, toErr(ctx, rerr)
|
||||
}
|
||||
|
||||
karesp = &LeaseKeepAliveResponse{
|
||||
ResponseHeader: resp.GetHeader(),
|
||||
ID: LeaseID(resp.ID),
|
||||
TTL: resp.TTL,
|
||||
}
|
||||
return karesp, nil
|
||||
}
|
||||
|
||||
func (l *lessor) recvKeepAliveLoop() (gerr error) {
|
||||
defer func() {
|
||||
l.mu.Lock()
|
||||
close(l.donec)
|
||||
l.loopErr = gerr
|
||||
for _, ka := range l.keepAlives {
|
||||
ka.close()
|
||||
}
|
||||
l.keepAlives = make(map[LeaseID]*keepAlive)
|
||||
l.mu.Unlock()
|
||||
}()
|
||||
|
||||
for {
|
||||
stream, err := l.resetRecv()
|
||||
if err != nil {
|
||||
if canceledByCaller(l.stopCtx, err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
if err != nil {
|
||||
if canceledByCaller(l.stopCtx, err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if toErr(l.stopCtx, err) == rpctypes.ErrNoLeader {
|
||||
l.closeRequireLeader()
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
l.recvKeepAlive(resp)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(retryConnWait):
|
||||
case <-l.stopCtx.Done():
|
||||
return l.stopCtx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resetRecv opens a new lease stream and starts sending keep alive requests.
|
||||
func (l *lessor) resetRecv() (pb.Lease_LeaseKeepAliveClient, error) {
|
||||
sctx, cancel := context.WithCancel(l.stopCtx)
|
||||
stream, err := l.remote.LeaseKeepAlive(sctx, append(l.callOpts, withMax(0))...)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.stream != nil && l.streamCancel != nil {
|
||||
l.streamCancel()
|
||||
}
|
||||
|
||||
l.streamCancel = cancel
|
||||
l.stream = stream
|
||||
|
||||
go l.sendKeepAliveLoop(stream)
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// recvKeepAlive updates a lease based on its LeaseKeepAliveResponse
|
||||
func (l *lessor) recvKeepAlive(resp *pb.LeaseKeepAliveResponse) {
|
||||
karesp := &LeaseKeepAliveResponse{
|
||||
ResponseHeader: resp.GetHeader(),
|
||||
ID: LeaseID(resp.ID),
|
||||
TTL: resp.TTL,
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
ka, ok := l.keepAlives[karesp.ID]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if karesp.TTL <= 0 {
|
||||
// lease expired; close all keep alive channels
|
||||
delete(l.keepAlives, karesp.ID)
|
||||
ka.close()
|
||||
return
|
||||
}
|
||||
|
||||
// send update to all channels
|
||||
nextKeepAlive := time.Now().Add((time.Duration(karesp.TTL) * time.Second) / 3.0)
|
||||
ka.deadline = time.Now().Add(time.Duration(karesp.TTL) * time.Second)
|
||||
for _, ch := range ka.chs {
|
||||
select {
|
||||
case ch <- karesp:
|
||||
default:
|
||||
if l.lg != nil {
|
||||
l.lg.Warn("lease keepalive response queue is full; dropping response send",
|
||||
zap.Int("queue-size", len(ch)),
|
||||
zap.Int("queue-capacity", cap(ch)),
|
||||
)
|
||||
}
|
||||
}
|
||||
// still advance in order to rate-limit keep-alive sends
|
||||
ka.nextKeepAlive = nextKeepAlive
|
||||
}
|
||||
}
|
||||
|
||||
// deadlineLoop reaps any keep alive channels that have not received a response
|
||||
// within the lease TTL
|
||||
func (l *lessor) deadlineLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
case <-l.donec:
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
l.mu.Lock()
|
||||
for id, ka := range l.keepAlives {
|
||||
if ka.deadline.Before(now) {
|
||||
// waited too long for response; lease may be expired
|
||||
ka.close()
|
||||
delete(l.keepAlives, id)
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// sendKeepAliveLoop sends keep alive requests for the lifetime of the given stream.
|
||||
func (l *lessor) sendKeepAliveLoop(stream pb.Lease_LeaseKeepAliveClient) {
|
||||
for {
|
||||
var tosend []LeaseID
|
||||
|
||||
now := time.Now()
|
||||
l.mu.Lock()
|
||||
for id, ka := range l.keepAlives {
|
||||
if ka.nextKeepAlive.Before(now) {
|
||||
tosend = append(tosend, id)
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
|
||||
for _, id := range tosend {
|
||||
r := &pb.LeaseKeepAliveRequest{ID: int64(id)}
|
||||
if err := stream.Send(r); err != nil {
|
||||
// TODO do something with this error?
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(retryConnWait):
|
||||
case <-stream.Context().Done():
|
||||
return
|
||||
case <-l.donec:
|
||||
return
|
||||
case <-l.stopCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ka *keepAlive) close() {
|
||||
close(ka.donec)
|
||||
for _, ch := range ka.chs {
|
||||
close(ch)
|
||||
}
|
||||
}
|
59
vendor/go.etcd.io/etcd/client/v3/logger.go
generated
vendored
Normal file
59
vendor/go.etcd.io/etcd/client/v3/logger.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"go.etcd.io/etcd/client/pkg/v3/logutil"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"go.uber.org/zap/zapgrpc"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We override grpc logger only when the environment variable is set
|
||||
// in order to not interfere by default with user's code or other libraries.
|
||||
if os.Getenv("ETCD_CLIENT_DEBUG") != "" {
|
||||
lg, err := logutil.CreateDefaultZapLogger(etcdClientDebugLevel())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lg = lg.Named("etcd-client")
|
||||
grpclog.SetLoggerV2(zapgrpc.NewLogger(lg))
|
||||
}
|
||||
}
|
||||
|
||||
// SetLogger sets grpc logger.
|
||||
//
|
||||
// Deprecated: use grpclog.SetLoggerV2 directly or grpc_zap.ReplaceGrpcLoggerV2.
|
||||
func SetLogger(l grpclog.LoggerV2) {
|
||||
grpclog.SetLoggerV2(l)
|
||||
}
|
||||
|
||||
// etcdClientDebugLevel translates ETCD_CLIENT_DEBUG into zap log level.
|
||||
func etcdClientDebugLevel() zapcore.Level {
|
||||
envLevel := os.Getenv("ETCD_CLIENT_DEBUG")
|
||||
if envLevel == "" || envLevel == "true" {
|
||||
return zapcore.InfoLevel
|
||||
}
|
||||
var l zapcore.Level
|
||||
if err := l.Set(envLevel); err != nil {
|
||||
log.Printf("Invalid value for environment variable 'ETCD_CLIENT_DEBUG'. Using default level: 'info'")
|
||||
return zapcore.InfoLevel
|
||||
}
|
||||
return l
|
||||
}
|
255
vendor/go.etcd.io/etcd/client/v3/maintenance.go
generated
vendored
Normal file
255
vendor/go.etcd.io/etcd/client/v3/maintenance.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type (
|
||||
DefragmentResponse pb.DefragmentResponse
|
||||
AlarmResponse pb.AlarmResponse
|
||||
AlarmMember pb.AlarmMember
|
||||
StatusResponse pb.StatusResponse
|
||||
HashKVResponse pb.HashKVResponse
|
||||
MoveLeaderResponse pb.MoveLeaderResponse
|
||||
)
|
||||
|
||||
type Maintenance interface {
|
||||
// AlarmList gets all active alarms.
|
||||
AlarmList(ctx context.Context) (*AlarmResponse, error)
|
||||
|
||||
// AlarmDisarm disarms a given alarm.
|
||||
AlarmDisarm(ctx context.Context, m *AlarmMember) (*AlarmResponse, error)
|
||||
|
||||
// Defragment releases wasted space from internal fragmentation on a given etcd member.
|
||||
// Defragment is only needed when deleting a large number of keys and want to reclaim
|
||||
// the resources.
|
||||
// Defragment is an expensive operation. User should avoid defragmenting multiple members
|
||||
// at the same time.
|
||||
// To defragment multiple members in the cluster, user need to call defragment multiple
|
||||
// times with different endpoints.
|
||||
Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error)
|
||||
|
||||
// Status gets the status of the endpoint.
|
||||
Status(ctx context.Context, endpoint string) (*StatusResponse, error)
|
||||
|
||||
// HashKV returns a hash of the KV state at the time of the RPC.
|
||||
// If revision is zero, the hash is computed on all keys. If the revision
|
||||
// is non-zero, the hash is computed on all keys at or below the given revision.
|
||||
HashKV(ctx context.Context, endpoint string, rev int64) (*HashKVResponse, error)
|
||||
|
||||
// Snapshot provides a reader for a point-in-time snapshot of etcd.
|
||||
// If the context "ctx" is canceled or timed out, reading from returned
|
||||
// "io.ReadCloser" would error out (e.g. context.Canceled, context.DeadlineExceeded).
|
||||
Snapshot(ctx context.Context) (io.ReadCloser, error)
|
||||
|
||||
// MoveLeader requests current leader to transfer its leadership to the transferee.
|
||||
// Request must be made to the leader.
|
||||
MoveLeader(ctx context.Context, transfereeID uint64) (*MoveLeaderResponse, error)
|
||||
}
|
||||
|
||||
type maintenance struct {
|
||||
lg *zap.Logger
|
||||
dial func(endpoint string) (pb.MaintenanceClient, func(), error)
|
||||
remote pb.MaintenanceClient
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
func NewMaintenance(c *Client) Maintenance {
|
||||
api := &maintenance{
|
||||
lg: c.lg,
|
||||
dial: func(endpoint string) (pb.MaintenanceClient, func(), error) {
|
||||
conn, err := c.Dial(endpoint)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to dial endpoint %s with maintenance client: %v", endpoint, err)
|
||||
}
|
||||
|
||||
//get token with established connection
|
||||
dctx := c.ctx
|
||||
cancel := func() {}
|
||||
if c.cfg.DialTimeout > 0 {
|
||||
dctx, cancel = context.WithTimeout(c.ctx, c.cfg.DialTimeout)
|
||||
}
|
||||
err = c.getToken(dctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, nil, fmt.Errorf("failed to getToken from endpoint %s with maintenance client: %v", endpoint, err)
|
||||
}
|
||||
cancel = func() { conn.Close() }
|
||||
return RetryMaintenanceClient(c, conn), cancel, nil
|
||||
},
|
||||
remote: RetryMaintenanceClient(c, c.conn),
|
||||
}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func NewMaintenanceFromMaintenanceClient(remote pb.MaintenanceClient, c *Client) Maintenance {
|
||||
api := &maintenance{
|
||||
lg: c.lg,
|
||||
dial: func(string) (pb.MaintenanceClient, func(), error) {
|
||||
return remote, func() {}, nil
|
||||
},
|
||||
remote: remote,
|
||||
}
|
||||
if c != nil {
|
||||
api.callOpts = c.callOpts
|
||||
}
|
||||
return api
|
||||
}
|
||||
|
||||
func (m *maintenance) AlarmList(ctx context.Context) (*AlarmResponse, error) {
|
||||
req := &pb.AlarmRequest{
|
||||
Action: pb.AlarmRequest_GET,
|
||||
MemberID: 0, // all
|
||||
Alarm: pb.AlarmType_NONE, // all
|
||||
}
|
||||
resp, err := m.remote.Alarm(ctx, req, m.callOpts...)
|
||||
if err == nil {
|
||||
return (*AlarmResponse)(resp), nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (m *maintenance) AlarmDisarm(ctx context.Context, am *AlarmMember) (*AlarmResponse, error) {
|
||||
req := &pb.AlarmRequest{
|
||||
Action: pb.AlarmRequest_DEACTIVATE,
|
||||
MemberID: am.MemberID,
|
||||
Alarm: am.Alarm,
|
||||
}
|
||||
|
||||
if req.MemberID == 0 && req.Alarm == pb.AlarmType_NONE {
|
||||
ar, err := m.AlarmList(ctx)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
ret := AlarmResponse{}
|
||||
for _, am := range ar.Alarms {
|
||||
dresp, derr := m.AlarmDisarm(ctx, (*AlarmMember)(am))
|
||||
if derr != nil {
|
||||
return nil, toErr(ctx, derr)
|
||||
}
|
||||
ret.Alarms = append(ret.Alarms, dresp.Alarms...)
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
resp, err := m.remote.Alarm(ctx, req, m.callOpts...)
|
||||
if err == nil {
|
||||
return (*AlarmResponse)(resp), nil
|
||||
}
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
|
||||
remote, cancel, err := m.dial(endpoint)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.Defragment(ctx, &pb.DefragmentRequest{}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*DefragmentResponse)(resp), nil
|
||||
}
|
||||
|
||||
func (m *maintenance) Status(ctx context.Context, endpoint string) (*StatusResponse, error) {
|
||||
remote, cancel, err := m.dial(endpoint)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.Status(ctx, &pb.StatusRequest{}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*StatusResponse)(resp), nil
|
||||
}
|
||||
|
||||
func (m *maintenance) HashKV(ctx context.Context, endpoint string, rev int64) (*HashKVResponse, error) {
|
||||
remote, cancel, err := m.dial(endpoint)
|
||||
if err != nil {
|
||||
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.HashKV(ctx, &pb.HashKVRequest{Revision: rev}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
return (*HashKVResponse)(resp), nil
|
||||
}
|
||||
|
||||
func (m *maintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
|
||||
ss, err := m.remote.Snapshot(ctx, &pb.SnapshotRequest{}, append(m.callOpts, withMax(defaultStreamMaxRetries))...)
|
||||
if err != nil {
|
||||
return nil, toErr(ctx, err)
|
||||
}
|
||||
|
||||
m.lg.Info("opened snapshot stream; downloading")
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
for {
|
||||
resp, err := ss.Recv()
|
||||
if err != nil {
|
||||
switch err {
|
||||
case io.EOF:
|
||||
m.lg.Info("completed snapshot read; closing")
|
||||
default:
|
||||
m.lg.Warn("failed to receive from snapshot stream; closing", zap.Error(err))
|
||||
}
|
||||
pw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// can "resp == nil && err == nil"
|
||||
// before we receive snapshot SHA digest?
|
||||
// No, server sends EOF with an empty response
|
||||
// after it sends SHA digest at the end
|
||||
|
||||
if _, werr := pw.Write(resp.Blob); werr != nil {
|
||||
pw.CloseWithError(werr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return &snapshotReadCloser{ctx: ctx, ReadCloser: pr}, nil
|
||||
}
|
||||
|
||||
type snapshotReadCloser struct {
|
||||
ctx context.Context
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
func (rc *snapshotReadCloser) Read(p []byte) (n int, err error) {
|
||||
n, err = rc.ReadCloser.Read(p)
|
||||
return n, toErr(rc.ctx, err)
|
||||
}
|
||||
|
||||
func (m *maintenance) MoveLeader(ctx context.Context, transfereeID uint64) (*MoveLeaderResponse, error) {
|
||||
resp, err := m.remote.MoveLeader(ctx, &pb.MoveLeaderRequest{TargetID: transfereeID}, m.callOpts...)
|
||||
return (*MoveLeaderResponse)(resp), toErr(ctx, err)
|
||||
}
|
583
vendor/go.etcd.io/etcd/client/v3/op.go
generated
vendored
Normal file
583
vendor/go.etcd.io/etcd/client/v3/op.go
generated
vendored
Normal file
@ -0,0 +1,583 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
|
||||
type opType int
|
||||
|
||||
const (
|
||||
// A default Op has opType 0, which is invalid.
|
||||
tRange opType = iota + 1
|
||||
tPut
|
||||
tDeleteRange
|
||||
tTxn
|
||||
)
|
||||
|
||||
var noPrefixEnd = []byte{0}
|
||||
|
||||
// Op represents an Operation that kv can execute.
|
||||
type Op struct {
|
||||
t opType
|
||||
key []byte
|
||||
end []byte
|
||||
|
||||
// for range
|
||||
limit int64
|
||||
sort *SortOption
|
||||
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
|
||||
|
||||
// for watch
|
||||
// fragmentation should be disabled by default
|
||||
// if true, split watch events when total exceeds
|
||||
// "--max-request-bytes" flag value + 512-byte
|
||||
fragment bool
|
||||
|
||||
// for put
|
||||
ignoreValue bool
|
||||
ignoreLease 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
|
||||
|
||||
// txn
|
||||
cmps []Cmp
|
||||
thenOps []Op
|
||||
elseOps []Op
|
||||
|
||||
isOptsWithFromKey bool
|
||||
isOptsWithPrefix bool
|
||||
}
|
||||
|
||||
// accessors / mutators
|
||||
|
||||
// IsTxn returns true if the "Op" type is transaction.
|
||||
func (op Op) IsTxn() bool {
|
||||
return op.t == tTxn
|
||||
}
|
||||
|
||||
// Txn returns the comparison(if) operations, "then" operations, and "else" operations.
|
||||
func (op Op) Txn() ([]Cmp, []Op, []Op) {
|
||||
return op.cmps, op.thenOps, op.elseOps
|
||||
}
|
||||
|
||||
// KeyBytes returns the byte slice holding the Op's key.
|
||||
func (op Op) KeyBytes() []byte { return op.key }
|
||||
|
||||
// WithKeyBytes sets the byte slice for the Op's key.
|
||||
func (op *Op) WithKeyBytes(key []byte) { op.key = key }
|
||||
|
||||
// RangeBytes returns the byte slice holding with the Op's range end, if any.
|
||||
func (op Op) RangeBytes() []byte { return op.end }
|
||||
|
||||
// Rev returns the requested revision, if any.
|
||||
func (op Op) Rev() int64 { return op.rev }
|
||||
|
||||
// IsPut returns true iff the operation is a Put.
|
||||
func (op Op) IsPut() bool { return op.t == tPut }
|
||||
|
||||
// IsGet returns true iff the operation is a Get.
|
||||
func (op Op) IsGet() bool { return op.t == tRange }
|
||||
|
||||
// IsDelete returns true iff the operation is a Delete.
|
||||
func (op Op) IsDelete() bool { return op.t == tDeleteRange }
|
||||
|
||||
// IsSerializable returns true if the serializable field is true.
|
||||
func (op Op) IsSerializable() bool { return op.serializable }
|
||||
|
||||
// IsKeysOnly returns whether keysOnly is set.
|
||||
func (op Op) IsKeysOnly() bool { return op.keysOnly }
|
||||
|
||||
// IsCountOnly returns whether countOnly is set.
|
||||
func (op Op) IsCountOnly() bool { return op.countOnly }
|
||||
|
||||
// MinModRev returns the operation's minimum modify revision.
|
||||
func (op Op) MinModRev() int64 { return op.minModRev }
|
||||
|
||||
// MaxModRev returns the operation's maximum modify revision.
|
||||
func (op Op) MaxModRev() int64 { return op.maxModRev }
|
||||
|
||||
// MinCreateRev returns the operation's minimum create revision.
|
||||
func (op Op) MinCreateRev() int64 { return op.minCreateRev }
|
||||
|
||||
// MaxCreateRev returns the operation's maximum create revision.
|
||||
func (op Op) MaxCreateRev() int64 { return op.maxCreateRev }
|
||||
|
||||
// WithRangeBytes sets the byte slice for the Op's range end.
|
||||
func (op *Op) WithRangeBytes(end []byte) { op.end = end }
|
||||
|
||||
// ValueBytes returns the byte slice holding the Op's value, if any.
|
||||
func (op Op) ValueBytes() []byte { return op.val }
|
||||
|
||||
// WithValueBytes sets the byte slice for the Op's value.
|
||||
func (op *Op) WithValueBytes(v []byte) { op.val = v }
|
||||
|
||||
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) toTxnRequest() *pb.TxnRequest {
|
||||
thenOps := make([]*pb.RequestOp, len(op.thenOps))
|
||||
for i, tOp := range op.thenOps {
|
||||
thenOps[i] = tOp.toRequestOp()
|
||||
}
|
||||
elseOps := make([]*pb.RequestOp, len(op.elseOps))
|
||||
for i, eOp := range op.elseOps {
|
||||
elseOps[i] = eOp.toRequestOp()
|
||||
}
|
||||
cmps := make([]*pb.Compare, len(op.cmps))
|
||||
for i := range op.cmps {
|
||||
cmps[i] = (*pb.Compare)(&op.cmps[i])
|
||||
}
|
||||
return &pb.TxnRequest{Compare: cmps, Success: thenOps, Failure: elseOps}
|
||||
}
|
||||
|
||||
func (op Op) toRequestOp() *pb.RequestOp {
|
||||
switch op.t {
|
||||
case tRange:
|
||||
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), PrevKv: op.prevKV, IgnoreValue: op.ignoreValue, IgnoreLease: op.ignoreLease}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
|
||||
case tDeleteRange:
|
||||
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
|
||||
case tTxn:
|
||||
return &pb.RequestOp{Request: &pb.RequestOp_RequestTxn{RequestTxn: op.toTxnRequest()}}
|
||||
default:
|
||||
panic("Unknown Op")
|
||||
}
|
||||
}
|
||||
|
||||
func (op Op) isWrite() bool {
|
||||
if op.t == tTxn {
|
||||
for _, tOp := range op.thenOps {
|
||||
if tOp.isWrite() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, tOp := range op.elseOps {
|
||||
if tOp.isWrite() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return op.t != tRange
|
||||
}
|
||||
|
||||
func NewOp() *Op {
|
||||
return &Op{key: []byte("")}
|
||||
}
|
||||
|
||||
// OpGet returns "get" operation based on given key and operation options.
|
||||
func OpGet(key string, opts ...OpOption) Op {
|
||||
// WithPrefix and WithFromKey are not supported together
|
||||
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
|
||||
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
|
||||
}
|
||||
ret := Op{t: tRange, key: []byte(key)}
|
||||
ret.applyOpts(opts)
|
||||
return ret
|
||||
}
|
||||
|
||||
// OpDelete returns "delete" operation based on given key and operation options.
|
||||
func OpDelete(key string, opts ...OpOption) Op {
|
||||
// WithPrefix and WithFromKey are not supported together
|
||||
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
|
||||
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
|
||||
}
|
||||
ret := Op{t: tDeleteRange, key: []byte(key)}
|
||||
ret.applyOpts(opts)
|
||||
switch {
|
||||
case ret.leaseID != 0:
|
||||
panic("unexpected lease in delete")
|
||||
case ret.limit != 0:
|
||||
panic("unexpected limit in delete")
|
||||
case ret.rev != 0:
|
||||
panic("unexpected revision in delete")
|
||||
case ret.sort != nil:
|
||||
panic("unexpected sort in delete")
|
||||
case ret.serializable:
|
||||
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
|
||||
}
|
||||
|
||||
// OpPut returns "put" operation based on given key-value and operation options.
|
||||
func OpPut(key, val string, opts ...OpOption) Op {
|
||||
ret := Op{t: tPut, key: []byte(key), val: []byte(val)}
|
||||
ret.applyOpts(opts)
|
||||
switch {
|
||||
case ret.end != nil:
|
||||
panic("unexpected range in put")
|
||||
case ret.limit != 0:
|
||||
panic("unexpected limit in put")
|
||||
case ret.rev != 0:
|
||||
panic("unexpected revision in put")
|
||||
case ret.sort != nil:
|
||||
panic("unexpected sort in put")
|
||||
case ret.serializable:
|
||||
panic("unexpected serializable in put")
|
||||
case ret.countOnly:
|
||||
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
|
||||
}
|
||||
|
||||
// OpTxn returns "txn" operation based on given transaction conditions.
|
||||
func OpTxn(cmps []Cmp, thenOps []Op, elseOps []Op) Op {
|
||||
return Op{t: tTxn, cmps: cmps, thenOps: thenOps, elseOps: elseOps}
|
||||
}
|
||||
|
||||
func opWatch(key string, opts ...OpOption) Op {
|
||||
ret := Op{t: tRange, key: []byte(key)}
|
||||
ret.applyOpts(opts)
|
||||
switch {
|
||||
case ret.leaseID != 0:
|
||||
panic("unexpected lease in watch")
|
||||
case ret.limit != 0:
|
||||
panic("unexpected limit in watch")
|
||||
case ret.sort != nil:
|
||||
panic("unexpected sort in watch")
|
||||
case ret.serializable:
|
||||
panic("unexpected serializable in watch")
|
||||
case ret.countOnly:
|
||||
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
|
||||
}
|
||||
|
||||
func (op *Op) applyOpts(opts []OpOption) {
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
}
|
||||
|
||||
// OpOption configures Operations like Get, Put, Delete.
|
||||
type OpOption func(*Op)
|
||||
|
||||
// WithLease attaches a lease ID to a key in 'Put' request.
|
||||
func WithLease(leaseID LeaseID) OpOption {
|
||||
return func(op *Op) { op.leaseID = leaseID }
|
||||
}
|
||||
|
||||
// WithLimit limits the number of results to return from 'Get' request.
|
||||
// If WithLimit is given a 0 limit, it is treated as no limit.
|
||||
func WithLimit(n int64) OpOption { return func(op *Op) { op.limit = n } }
|
||||
|
||||
// WithRev specifies the store revision for 'Get' request.
|
||||
// Or the start revision of 'Watch' request.
|
||||
func WithRev(rev int64) OpOption { return func(op *Op) { op.rev = rev } }
|
||||
|
||||
// WithSort specifies the ordering in 'Get' request. It requires
|
||||
// 'WithRange' and/or 'WithPrefix' to be specified too.
|
||||
// 'target' specifies the target to sort by: key, version, revisions, value.
|
||||
// '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 by default the server returns results sorted by keys
|
||||
// in lexicographically ascending order, the client should ignore
|
||||
// SortOrder if the target is SortByKey.
|
||||
order = SortNone
|
||||
}
|
||||
op.sort = &SortOption{target, order}
|
||||
}
|
||||
}
|
||||
|
||||
// GetPrefixRangeEnd gets the range end of the prefix.
|
||||
// 'Get(foo, WithPrefix())' is equal to 'Get(foo, WithRange(GetPrefixRangeEnd(foo))'.
|
||||
func GetPrefixRangeEnd(prefix string) string {
|
||||
return string(getPrefix([]byte(prefix)))
|
||||
}
|
||||
|
||||
func getPrefix(key []byte) []byte {
|
||||
end := make([]byte, len(key))
|
||||
copy(end, key)
|
||||
for i := len(end) - 1; i >= 0; i-- {
|
||||
if end[i] < 0xff {
|
||||
end[i] = end[i] + 1
|
||||
end = end[:i+1]
|
||||
return end
|
||||
}
|
||||
}
|
||||
// next prefix does not exist (e.g., 0xffff);
|
||||
// default to WithFromKey policy
|
||||
return noPrefixEnd
|
||||
}
|
||||
|
||||
// WithPrefix enables 'Get', 'Delete', or 'Watch' requests to operate
|
||||
// on the keys with matching prefix. For example, 'Get(foo, WithPrefix())'
|
||||
// can return 'foo1', 'foo2', and so on.
|
||||
func WithPrefix() OpOption {
|
||||
return func(op *Op) {
|
||||
op.isOptsWithPrefix = true
|
||||
if len(op.key) == 0 {
|
||||
op.key, op.end = []byte{0}, []byte{0}
|
||||
return
|
||||
}
|
||||
op.end = getPrefix(op.key)
|
||||
}
|
||||
}
|
||||
|
||||
// WithRange specifies the range of 'Get', 'Delete', 'Watch' requests.
|
||||
// For example, 'Get' requests with 'WithRange(end)' returns
|
||||
// the keys in the range [key, end).
|
||||
// endKey must be lexicographically greater than start key.
|
||||
func WithRange(endKey string) OpOption {
|
||||
return func(op *Op) { op.end = []byte(endKey) }
|
||||
}
|
||||
|
||||
// WithFromKey specifies the range of 'Get', 'Delete', 'Watch' requests
|
||||
// to be equal or greater than the key in the argument.
|
||||
func WithFromKey() OpOption {
|
||||
return func(op *Op) {
|
||||
if len(op.key) == 0 {
|
||||
op.key = []byte{0}
|
||||
}
|
||||
op.end = []byte("\x00")
|
||||
op.isOptsWithFromKey = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithSerializable makes 'Get' request serializable. By default,
|
||||
// it's linearizable. Serializable requests are better for lower latency
|
||||
// requirement.
|
||||
func WithSerializable() OpOption {
|
||||
return func(op *Op) { op.serializable = true }
|
||||
}
|
||||
|
||||
// WithKeysOnly makes the 'Get' request return only the keys and the corresponding
|
||||
// values will be omitted.
|
||||
func WithKeysOnly() OpOption {
|
||||
return func(op *Op) { op.keysOnly = true }
|
||||
}
|
||||
|
||||
// WithCountOnly makes the 'Get' request return only the count of keys.
|
||||
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) }
|
||||
|
||||
// WithLastCreate gets the key with the latest creation revision in the request range.
|
||||
func WithLastCreate() []OpOption { return withTop(SortByCreateRevision, SortDescend) }
|
||||
|
||||
// WithFirstKey gets the lexically first key in the request range.
|
||||
func WithFirstKey() []OpOption { return withTop(SortByKey, SortAscend) }
|
||||
|
||||
// WithLastKey gets the lexically last key in the request range.
|
||||
func WithLastKey() []OpOption { return withTop(SortByKey, SortDescend) }
|
||||
|
||||
// WithFirstRev gets the key with the oldest modification revision in the request range.
|
||||
func WithFirstRev() []OpOption { return withTop(SortByModRevision, SortAscend) }
|
||||
|
||||
// WithLastRev gets the key with the latest modification revision in the request range.
|
||||
func WithLastRev() []OpOption { return withTop(SortByModRevision, SortDescend) }
|
||||
|
||||
// withTop gets the first key over the get's prefix given a sort order
|
||||
func withTop(target SortTarget, order SortOrder) []OpOption {
|
||||
return []OpOption{WithPrefix(), WithSort(target, order), WithLimit(1)}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// WithFragment to receive raw watch response with fragmentation.
|
||||
// Fragmentation is disabled by default. If fragmentation is enabled,
|
||||
// etcd watch server will split watch response before sending to clients
|
||||
// when the total size of watch events exceed server-side request limit.
|
||||
// The default server-side request limit is 1.5 MiB, which can be configured
|
||||
// as "--max-request-bytes" flag value + gRPC-overhead 512 bytes.
|
||||
// See "etcdserver/api/v3rpc/watch.go" for more details.
|
||||
func WithFragment() OpOption {
|
||||
return func(op *Op) { op.fragment = true }
|
||||
}
|
||||
|
||||
// WithIgnoreValue updates the key using its current value.
|
||||
// This option can not be combined with non-empty values.
|
||||
// Returns an error if the key does not exist.
|
||||
func WithIgnoreValue() OpOption {
|
||||
return func(op *Op) {
|
||||
op.ignoreValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithIgnoreLease updates the key using its current lease.
|
||||
// This option can not be combined with WithLease.
|
||||
// Returns an error if the key does not exist.
|
||||
func WithIgnoreLease() OpOption {
|
||||
return func(op *Op) {
|
||||
op.ignoreLease = 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 makes TimeToLive list the keys attached to the 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}
|
||||
}
|
||||
|
||||
// IsOptsWithPrefix returns true if WithPrefix option is called in the given opts.
|
||||
func IsOptsWithPrefix(opts []OpOption) bool {
|
||||
ret := NewOp()
|
||||
for _, opt := range opts {
|
||||
opt(ret)
|
||||
}
|
||||
|
||||
return ret.isOptsWithPrefix
|
||||
}
|
||||
|
||||
// IsOptsWithFromKey returns true if WithFromKey option is called in the given opts.
|
||||
func IsOptsWithFromKey(opts []OpOption) bool {
|
||||
ret := NewOp()
|
||||
for _, opt := range opts {
|
||||
opt(ret)
|
||||
}
|
||||
|
||||
return ret.isOptsWithFromKey
|
||||
}
|
69
vendor/go.etcd.io/etcd/client/v3/options.go
generated
vendored
Normal file
69
vendor/go.etcd.io/etcd/client/v3/options.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2017 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
// client-side handling retrying of request failures where data was not written to the wire or
|
||||
// where server indicates it did not process the data. gRPC default is default is "WaitForReady(false)"
|
||||
// but for etcd we default to "WaitForReady(true)" to minimize client request error responses due to
|
||||
// transient failures.
|
||||
defaultWaitForReady = grpc.WaitForReady(true)
|
||||
|
||||
// client-side request send limit, gRPC default is math.MaxInt32
|
||||
// Make sure that "client-side send limit < server-side default send/recv limit"
|
||||
// Same value as "embed.DefaultMaxRequestBytes" plus gRPC overhead bytes
|
||||
defaultMaxCallSendMsgSize = grpc.MaxCallSendMsgSize(2 * 1024 * 1024)
|
||||
|
||||
// client-side response receive limit, gRPC default is 4MB
|
||||
// Make sure that "client-side receive limit >= server-side default send/recv limit"
|
||||
// because range response can easily exceed request send limits
|
||||
// Default to math.MaxInt32; writes exceeding server-side send limit fails anyway
|
||||
defaultMaxCallRecvMsgSize = grpc.MaxCallRecvMsgSize(math.MaxInt32)
|
||||
|
||||
// client-side non-streaming retry limit, only applied to requests where server responds with
|
||||
// a error code clearly indicating it was unable to process the request such as codes.Unavailable.
|
||||
// If set to 0, retry is disabled.
|
||||
defaultUnaryMaxRetries uint = 100
|
||||
|
||||
// client-side streaming retry limit, only applied to requests where server responds with
|
||||
// a error code clearly indicating it was unable to process the request such as codes.Unavailable.
|
||||
// If set to 0, retry is disabled.
|
||||
defaultStreamMaxRetries = ^uint(0) // max uint
|
||||
|
||||
// client-side retry backoff wait between requests.
|
||||
defaultBackoffWaitBetween = 25 * time.Millisecond
|
||||
|
||||
// client-side retry backoff default jitter fraction.
|
||||
defaultBackoffJitterFraction = 0.10
|
||||
)
|
||||
|
||||
// defaultCallOpts defines a list of default "gRPC.CallOption".
|
||||
// Some options are exposed to "clientv3.Config".
|
||||
// Defaults will be overridden by the settings in "clientv3.Config".
|
||||
var defaultCallOpts = []grpc.CallOption{
|
||||
defaultWaitForReady,
|
||||
defaultMaxCallSendMsgSize,
|
||||
defaultMaxCallRecvMsgSize,
|
||||
}
|
||||
|
||||
// MaxLeaseTTL is the maximum lease TTL value
|
||||
const MaxLeaseTTL = 9000000000
|
306
vendor/go.etcd.io/etcd/client/v3/retry.go
generated
vendored
Normal file
306
vendor/go.etcd.io/etcd/client/v3/retry.go
generated
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type retryPolicy uint8
|
||||
|
||||
const (
|
||||
repeatable retryPolicy = iota
|
||||
nonRepeatable
|
||||
)
|
||||
|
||||
func (rp retryPolicy) String() string {
|
||||
switch rp {
|
||||
case repeatable:
|
||||
return "repeatable"
|
||||
case nonRepeatable:
|
||||
return "nonRepeatable"
|
||||
default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
// isSafeRetryImmutableRPC returns "true" when an immutable request is safe for retry.
|
||||
//
|
||||
// immutable requests (e.g. Get) should be retried unless it's
|
||||
// an obvious server-side error (e.g. rpctypes.ErrRequestTooLarge).
|
||||
//
|
||||
// Returning "false" means retry should stop, since client cannot
|
||||
// handle itself even with retries.
|
||||
func isSafeRetryImmutableRPC(err error) bool {
|
||||
eErr := rpctypes.Error(err)
|
||||
if serverErr, ok := eErr.(rpctypes.EtcdError); ok && serverErr.Code() != codes.Unavailable {
|
||||
// interrupted by non-transient server-side or gRPC-side error
|
||||
// client cannot handle itself (e.g. rpctypes.ErrCompacted)
|
||||
return false
|
||||
}
|
||||
// only retry if unavailable
|
||||
ev, ok := status.FromError(err)
|
||||
if !ok {
|
||||
// all errors from RPC is typed "grpc/status.(*statusError)"
|
||||
// (ref. https://github.com/grpc/grpc-go/pull/1782)
|
||||
//
|
||||
// if the error type is not "grpc/status.(*statusError)",
|
||||
// it could be from "Dial"
|
||||
// TODO: do not retry for now
|
||||
// ref. https://github.com/grpc/grpc-go/issues/1581
|
||||
return false
|
||||
}
|
||||
return ev.Code() == codes.Unavailable
|
||||
}
|
||||
|
||||
// isSafeRetryMutableRPC returns "true" when a mutable request is safe for retry.
|
||||
//
|
||||
// mutable requests (e.g. Put, Delete, Txn) should only be retried
|
||||
// when the status code is codes.Unavailable when initial connection
|
||||
// has not been established (no endpoint is up).
|
||||
//
|
||||
// Returning "false" means retry should stop, otherwise it violates
|
||||
// write-at-most-once semantics.
|
||||
func isSafeRetryMutableRPC(err error) bool {
|
||||
if ev, ok := status.FromError(err); ok && ev.Code() != codes.Unavailable {
|
||||
// not safe for mutable RPCs
|
||||
// e.g. interrupted by non-transient error that client cannot handle itself,
|
||||
// or transient error while the connection has already been established
|
||||
return false
|
||||
}
|
||||
desc := rpctypes.ErrorDesc(err)
|
||||
return desc == "there is no address available" || desc == "there is no connection available"
|
||||
}
|
||||
|
||||
type retryKVClient struct {
|
||||
kc pb.KVClient
|
||||
}
|
||||
|
||||
// RetryKVClient implements a KVClient.
|
||||
func RetryKVClient(c *Client) pb.KVClient {
|
||||
return &retryKVClient{
|
||||
kc: pb.NewKVClient(c.conn),
|
||||
}
|
||||
}
|
||||
func (rkv *retryKVClient) Range(ctx context.Context, in *pb.RangeRequest, opts ...grpc.CallOption) (resp *pb.RangeResponse, err error) {
|
||||
return rkv.kc.Range(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rkv *retryKVClient) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (resp *pb.PutResponse, err error) {
|
||||
return rkv.kc.Put(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rkv *retryKVClient) DeleteRange(ctx context.Context, in *pb.DeleteRangeRequest, opts ...grpc.CallOption) (resp *pb.DeleteRangeResponse, err error) {
|
||||
return rkv.kc.DeleteRange(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rkv *retryKVClient) Txn(ctx context.Context, in *pb.TxnRequest, opts ...grpc.CallOption) (resp *pb.TxnResponse, err error) {
|
||||
return rkv.kc.Txn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rkv *retryKVClient) Compact(ctx context.Context, in *pb.CompactionRequest, opts ...grpc.CallOption) (resp *pb.CompactionResponse, err error) {
|
||||
return rkv.kc.Compact(ctx, in, opts...)
|
||||
}
|
||||
|
||||
type retryLeaseClient struct {
|
||||
lc pb.LeaseClient
|
||||
}
|
||||
|
||||
// RetryLeaseClient implements a LeaseClient.
|
||||
func RetryLeaseClient(c *Client) pb.LeaseClient {
|
||||
return &retryLeaseClient{
|
||||
lc: pb.NewLeaseClient(c.conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (rlc *retryLeaseClient) LeaseTimeToLive(ctx context.Context, in *pb.LeaseTimeToLiveRequest, opts ...grpc.CallOption) (resp *pb.LeaseTimeToLiveResponse, err error) {
|
||||
return rlc.lc.LeaseTimeToLive(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rlc *retryLeaseClient) LeaseLeases(ctx context.Context, in *pb.LeaseLeasesRequest, opts ...grpc.CallOption) (resp *pb.LeaseLeasesResponse, err error) {
|
||||
return rlc.lc.LeaseLeases(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rlc *retryLeaseClient) LeaseGrant(ctx context.Context, in *pb.LeaseGrantRequest, opts ...grpc.CallOption) (resp *pb.LeaseGrantResponse, err error) {
|
||||
return rlc.lc.LeaseGrant(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rlc *retryLeaseClient) LeaseRevoke(ctx context.Context, in *pb.LeaseRevokeRequest, opts ...grpc.CallOption) (resp *pb.LeaseRevokeResponse, err error) {
|
||||
return rlc.lc.LeaseRevoke(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rlc *retryLeaseClient) LeaseKeepAlive(ctx context.Context, opts ...grpc.CallOption) (stream pb.Lease_LeaseKeepAliveClient, err error) {
|
||||
return rlc.lc.LeaseKeepAlive(ctx, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
type retryClusterClient struct {
|
||||
cc pb.ClusterClient
|
||||
}
|
||||
|
||||
// RetryClusterClient implements a ClusterClient.
|
||||
func RetryClusterClient(c *Client) pb.ClusterClient {
|
||||
return &retryClusterClient{
|
||||
cc: pb.NewClusterClient(c.conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (rcc *retryClusterClient) MemberList(ctx context.Context, in *pb.MemberListRequest, opts ...grpc.CallOption) (resp *pb.MemberListResponse, err error) {
|
||||
return rcc.cc.MemberList(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rcc *retryClusterClient) MemberAdd(ctx context.Context, in *pb.MemberAddRequest, opts ...grpc.CallOption) (resp *pb.MemberAddResponse, err error) {
|
||||
return rcc.cc.MemberAdd(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rcc *retryClusterClient) MemberRemove(ctx context.Context, in *pb.MemberRemoveRequest, opts ...grpc.CallOption) (resp *pb.MemberRemoveResponse, err error) {
|
||||
return rcc.cc.MemberRemove(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rcc *retryClusterClient) MemberUpdate(ctx context.Context, in *pb.MemberUpdateRequest, opts ...grpc.CallOption) (resp *pb.MemberUpdateResponse, err error) {
|
||||
return rcc.cc.MemberUpdate(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rcc *retryClusterClient) MemberPromote(ctx context.Context, in *pb.MemberPromoteRequest, opts ...grpc.CallOption) (resp *pb.MemberPromoteResponse, err error) {
|
||||
return rcc.cc.MemberPromote(ctx, in, opts...)
|
||||
}
|
||||
|
||||
type retryMaintenanceClient struct {
|
||||
mc pb.MaintenanceClient
|
||||
}
|
||||
|
||||
// RetryMaintenanceClient implements a Maintenance.
|
||||
func RetryMaintenanceClient(c *Client, conn *grpc.ClientConn) pb.MaintenanceClient {
|
||||
return &retryMaintenanceClient{
|
||||
mc: pb.NewMaintenanceClient(conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Alarm(ctx context.Context, in *pb.AlarmRequest, opts ...grpc.CallOption) (resp *pb.AlarmResponse, err error) {
|
||||
return rmc.mc.Alarm(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Status(ctx context.Context, in *pb.StatusRequest, opts ...grpc.CallOption) (resp *pb.StatusResponse, err error) {
|
||||
return rmc.mc.Status(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Hash(ctx context.Context, in *pb.HashRequest, opts ...grpc.CallOption) (resp *pb.HashResponse, err error) {
|
||||
return rmc.mc.Hash(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) HashKV(ctx context.Context, in *pb.HashKVRequest, opts ...grpc.CallOption) (resp *pb.HashKVResponse, err error) {
|
||||
return rmc.mc.HashKV(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Snapshot(ctx context.Context, in *pb.SnapshotRequest, opts ...grpc.CallOption) (stream pb.Maintenance_SnapshotClient, err error) {
|
||||
return rmc.mc.Snapshot(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) MoveLeader(ctx context.Context, in *pb.MoveLeaderRequest, opts ...grpc.CallOption) (resp *pb.MoveLeaderResponse, err error) {
|
||||
return rmc.mc.MoveLeader(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Defragment(ctx context.Context, in *pb.DefragmentRequest, opts ...grpc.CallOption) (resp *pb.DefragmentResponse, err error) {
|
||||
return rmc.mc.Defragment(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rmc *retryMaintenanceClient) Downgrade(ctx context.Context, in *pb.DowngradeRequest, opts ...grpc.CallOption) (resp *pb.DowngradeResponse, err error) {
|
||||
return rmc.mc.Downgrade(ctx, in, opts...)
|
||||
}
|
||||
|
||||
type retryAuthClient struct {
|
||||
ac pb.AuthClient
|
||||
}
|
||||
|
||||
// RetryAuthClient implements a AuthClient.
|
||||
func RetryAuthClient(c *Client) pb.AuthClient {
|
||||
return &retryAuthClient{
|
||||
ac: pb.NewAuthClient(c.conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserList(ctx context.Context, in *pb.AuthUserListRequest, opts ...grpc.CallOption) (resp *pb.AuthUserListResponse, err error) {
|
||||
return rac.ac.UserList(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserGet(ctx context.Context, in *pb.AuthUserGetRequest, opts ...grpc.CallOption) (resp *pb.AuthUserGetResponse, err error) {
|
||||
return rac.ac.UserGet(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleGet(ctx context.Context, in *pb.AuthRoleGetRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleGetResponse, err error) {
|
||||
return rac.ac.RoleGet(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleList(ctx context.Context, in *pb.AuthRoleListRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleListResponse, err error) {
|
||||
return rac.ac.RoleList(ctx, in, append(opts, withRetryPolicy(repeatable))...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) AuthEnable(ctx context.Context, in *pb.AuthEnableRequest, opts ...grpc.CallOption) (resp *pb.AuthEnableResponse, err error) {
|
||||
return rac.ac.AuthEnable(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) AuthDisable(ctx context.Context, in *pb.AuthDisableRequest, opts ...grpc.CallOption) (resp *pb.AuthDisableResponse, err error) {
|
||||
return rac.ac.AuthDisable(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) AuthStatus(ctx context.Context, in *pb.AuthStatusRequest, opts ...grpc.CallOption) (resp *pb.AuthStatusResponse, err error) {
|
||||
return rac.ac.AuthStatus(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserAdd(ctx context.Context, in *pb.AuthUserAddRequest, opts ...grpc.CallOption) (resp *pb.AuthUserAddResponse, err error) {
|
||||
return rac.ac.UserAdd(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserDelete(ctx context.Context, in *pb.AuthUserDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthUserDeleteResponse, err error) {
|
||||
return rac.ac.UserDelete(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserChangePassword(ctx context.Context, in *pb.AuthUserChangePasswordRequest, opts ...grpc.CallOption) (resp *pb.AuthUserChangePasswordResponse, err error) {
|
||||
return rac.ac.UserChangePassword(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserGrantRole(ctx context.Context, in *pb.AuthUserGrantRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserGrantRoleResponse, err error) {
|
||||
return rac.ac.UserGrantRole(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) UserRevokeRole(ctx context.Context, in *pb.AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserRevokeRoleResponse, err error) {
|
||||
return rac.ac.UserRevokeRole(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleAdd(ctx context.Context, in *pb.AuthRoleAddRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleAddResponse, err error) {
|
||||
return rac.ac.RoleAdd(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleDelete(ctx context.Context, in *pb.AuthRoleDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleDeleteResponse, err error) {
|
||||
return rac.ac.RoleDelete(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleGrantPermission(ctx context.Context, in *pb.AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleGrantPermissionResponse, err error) {
|
||||
return rac.ac.RoleGrantPermission(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) RoleRevokePermission(ctx context.Context, in *pb.AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleRevokePermissionResponse, err error) {
|
||||
return rac.ac.RoleRevokePermission(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (rac *retryAuthClient) Authenticate(ctx context.Context, in *pb.AuthenticateRequest, opts ...grpc.CallOption) (resp *pb.AuthenticateResponse, err error) {
|
||||
return rac.ac.Authenticate(ctx, in, opts...)
|
||||
}
|
433
vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go
generated
vendored
Normal file
433
vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go
generated
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
// 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.
|
||||
|
||||
// Based on github.com/grpc-ecosystem/go-grpc-middleware/retry, but modified to support the more
|
||||
// fine grained error checking required by write-at-most-once retry semantics of etcd.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// unaryClientInterceptor returns a new retrying unary client interceptor.
|
||||
//
|
||||
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
func (c *Client) unaryClientInterceptor(optFuncs ...retryOption) grpc.UnaryClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
ctx = withVersion(ctx)
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
if callOpts.max == 0 {
|
||||
return invoker(ctx, method, req, reply, cc, grpcOpts...)
|
||||
}
|
||||
var lastErr error
|
||||
for attempt := uint(0); attempt < callOpts.max; attempt++ {
|
||||
if err := waitRetryBackoff(ctx, attempt, callOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
c.GetLogger().Debug(
|
||||
"retrying of unary invoker",
|
||||
zap.String("target", cc.Target()),
|
||||
zap.Uint("attempt", attempt),
|
||||
)
|
||||
lastErr = invoker(ctx, method, req, reply, cc, grpcOpts...)
|
||||
if lastErr == nil {
|
||||
return nil
|
||||
}
|
||||
c.GetLogger().Warn(
|
||||
"retrying of unary invoker failed",
|
||||
zap.String("target", cc.Target()),
|
||||
zap.Uint("attempt", attempt),
|
||||
zap.Error(lastErr),
|
||||
)
|
||||
if isContextError(lastErr) {
|
||||
if ctx.Err() != nil {
|
||||
// its the context deadline or cancellation.
|
||||
return lastErr
|
||||
}
|
||||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
continue
|
||||
}
|
||||
if c.shouldRefreshToken(lastErr, callOpts) {
|
||||
gterr := c.refreshToken(ctx)
|
||||
if gterr != nil {
|
||||
c.GetLogger().Warn(
|
||||
"retrying of unary invoker failed to fetch new auth token",
|
||||
zap.String("target", cc.Target()),
|
||||
zap.Error(gterr),
|
||||
)
|
||||
return gterr // lastErr must be invalid auth token
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !isSafeRetry(c.lg, lastErr, callOpts) {
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
|
||||
// streamClientInterceptor returns a new retrying stream client interceptor for server side streaming calls.
|
||||
//
|
||||
// The default configuration of the interceptor is to not retry *at all*. This behaviour can be
|
||||
// changed through options (e.g. WithMax) on creation of the interceptor or on call (through grpc.CallOptions).
|
||||
//
|
||||
// Retry logic is available *only for ServerStreams*, i.e. 1:n streams, as the internal logic needs
|
||||
// to buffer the messages sent by the client. If retry is enabled on any other streams (ClientStreams,
|
||||
// BidiStreams), the retry interceptor will fail the call.
|
||||
func (c *Client) streamClientInterceptor(optFuncs ...retryOption) grpc.StreamClientInterceptor {
|
||||
intOpts := reuseOrNewWithCallOptions(defaultOptions, optFuncs)
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
ctx = withVersion(ctx)
|
||||
// getToken automatically
|
||||
// TODO(cfc4n): keep this code block, remove codes about getToken in client.go after pr #12165 merged.
|
||||
if c.authTokenBundle != nil {
|
||||
// equal to c.Username != "" && c.Password != ""
|
||||
err := c.getToken(ctx)
|
||||
if err != nil && rpctypes.Error(err) != rpctypes.ErrAuthNotEnabled {
|
||||
c.GetLogger().Error("clientv3/retry_interceptor: getToken failed", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
grpcOpts, retryOpts := filterCallOptions(opts)
|
||||
callOpts := reuseOrNewWithCallOptions(intOpts, retryOpts)
|
||||
// short circuit for simplicity, and avoiding allocations.
|
||||
if callOpts.max == 0 {
|
||||
return streamer(ctx, desc, cc, method, grpcOpts...)
|
||||
}
|
||||
if desc.ClientStreams {
|
||||
return nil, status.Errorf(codes.Unimplemented, "clientv3/retry_interceptor: cannot retry on ClientStreams, set Disable()")
|
||||
}
|
||||
newStreamer, err := streamer(ctx, desc, cc, method, grpcOpts...)
|
||||
if err != nil {
|
||||
c.GetLogger().Error("streamer failed to create ClientStream", zap.Error(err))
|
||||
return nil, err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
}
|
||||
retryingStreamer := &serverStreamingRetryingStream{
|
||||
client: c,
|
||||
ClientStream: newStreamer,
|
||||
callOpts: callOpts,
|
||||
ctx: ctx,
|
||||
streamerCall: func(ctx context.Context) (grpc.ClientStream, error) {
|
||||
return streamer(ctx, desc, cc, method, grpcOpts...)
|
||||
},
|
||||
}
|
||||
return retryingStreamer, nil
|
||||
}
|
||||
}
|
||||
|
||||
// shouldRefreshToken checks whether there's a need to refresh the token based on the error and callOptions,
|
||||
// and returns a boolean value.
|
||||
func (c *Client) shouldRefreshToken(err error, callOpts *options) bool {
|
||||
if rpctypes.Error(err) == rpctypes.ErrUserEmpty {
|
||||
// refresh the token when username, password is present but the server returns ErrUserEmpty
|
||||
// which is possible when the client token is cleared somehow
|
||||
return c.authTokenBundle != nil // equal to c.Username != "" && c.Password != ""
|
||||
}
|
||||
|
||||
return callOpts.retryAuth &&
|
||||
(rpctypes.Error(err) == rpctypes.ErrInvalidAuthToken || rpctypes.Error(err) == rpctypes.ErrAuthOldRevision)
|
||||
}
|
||||
|
||||
func (c *Client) refreshToken(ctx context.Context) error {
|
||||
if c.authTokenBundle == nil {
|
||||
// c.authTokenBundle will be initialized only when
|
||||
// c.Username != "" && c.Password != "".
|
||||
//
|
||||
// When users use the TLS CommonName based authentication, the
|
||||
// authTokenBundle is always nil. But it's possible for the clients
|
||||
// to get `rpctypes.ErrAuthOldRevision` response when the clients
|
||||
// concurrently modify auth data (e.g, addUser, deleteUser etc.).
|
||||
// In this case, there is no need to refresh the token; instead the
|
||||
// clients just need to retry the operations (e.g. Put, Delete etc).
|
||||
return nil
|
||||
}
|
||||
// clear auth token before refreshing it.
|
||||
c.authTokenBundle.UpdateAuthToken("")
|
||||
return c.getToken(ctx)
|
||||
}
|
||||
|
||||
// type serverStreamingRetryingStream is the implementation of grpc.ClientStream that acts as a
|
||||
// proxy to the underlying call. If any of the RecvMsg() calls fail, it will try to reestablish
|
||||
// a new ClientStream according to the retry policy.
|
||||
type serverStreamingRetryingStream struct {
|
||||
grpc.ClientStream
|
||||
client *Client
|
||||
bufferedSends []interface{} // single message that the client can sen
|
||||
receivedGood bool // indicates whether any prior receives were successful
|
||||
wasClosedSend bool // indicates that CloseSend was closed
|
||||
ctx context.Context
|
||||
callOpts *options
|
||||
streamerCall func(ctx context.Context) (grpc.ClientStream, error)
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) setStream(clientStream grpc.ClientStream) {
|
||||
s.mu.Lock()
|
||||
s.ClientStream = clientStream
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) getStream() grpc.ClientStream {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.ClientStream
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) SendMsg(m interface{}) error {
|
||||
s.mu.Lock()
|
||||
s.bufferedSends = append(s.bufferedSends, m)
|
||||
s.mu.Unlock()
|
||||
return s.getStream().SendMsg(m)
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) CloseSend() error {
|
||||
s.mu.Lock()
|
||||
s.wasClosedSend = true
|
||||
s.mu.Unlock()
|
||||
return s.getStream().CloseSend()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) Header() (metadata.MD, error) {
|
||||
return s.getStream().Header()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) Trailer() metadata.MD {
|
||||
return s.getStream().Trailer()
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) RecvMsg(m interface{}) error {
|
||||
attemptRetry, lastErr := s.receiveMsgAndIndicateRetry(m)
|
||||
if !attemptRetry {
|
||||
return lastErr // success or hard failure
|
||||
}
|
||||
|
||||
// We start off from attempt 1, because zeroth was already made on normal SendMsg().
|
||||
for attempt := uint(1); attempt < s.callOpts.max; attempt++ {
|
||||
if err := waitRetryBackoff(s.ctx, attempt, s.callOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
newStream, err := s.reestablishStreamAndResendBuffer(s.ctx)
|
||||
if err != nil {
|
||||
s.client.lg.Error("failed reestablishStreamAndResendBuffer", zap.Error(err))
|
||||
return err // TODO(mwitkow): Maybe dial and transport errors should be retriable?
|
||||
}
|
||||
s.setStream(newStream)
|
||||
|
||||
s.client.lg.Warn("retrying RecvMsg", zap.Error(lastErr))
|
||||
attemptRetry, lastErr = s.receiveMsgAndIndicateRetry(m)
|
||||
if !attemptRetry {
|
||||
return lastErr
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) receiveMsgAndIndicateRetry(m interface{}) (bool, error) {
|
||||
s.mu.RLock()
|
||||
wasGood := s.receivedGood
|
||||
s.mu.RUnlock()
|
||||
err := s.getStream().RecvMsg(m)
|
||||
if err == nil || err == io.EOF {
|
||||
s.mu.Lock()
|
||||
s.receivedGood = true
|
||||
s.mu.Unlock()
|
||||
return false, err
|
||||
} else if wasGood {
|
||||
// previous RecvMsg in the stream succeeded, no retry logic should interfere
|
||||
return false, err
|
||||
}
|
||||
if isContextError(err) {
|
||||
if s.ctx.Err() != nil {
|
||||
return false, err
|
||||
}
|
||||
// its the callCtx deadline or cancellation, in which case try again.
|
||||
return true, err
|
||||
}
|
||||
if s.client.shouldRefreshToken(err, s.callOpts) {
|
||||
gterr := s.client.refreshToken(s.ctx)
|
||||
if gterr != nil {
|
||||
s.client.lg.Warn("retry failed to fetch new auth token", zap.Error(gterr))
|
||||
return false, err // return the original error for simplicity
|
||||
}
|
||||
return true, err
|
||||
|
||||
}
|
||||
return isSafeRetry(s.client.lg, err, s.callOpts), err
|
||||
}
|
||||
|
||||
func (s *serverStreamingRetryingStream) reestablishStreamAndResendBuffer(callCtx context.Context) (grpc.ClientStream, error) {
|
||||
s.mu.RLock()
|
||||
bufferedSends := s.bufferedSends
|
||||
s.mu.RUnlock()
|
||||
newStream, err := s.streamerCall(callCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, msg := range bufferedSends {
|
||||
if err := newStream.SendMsg(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := newStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStream, nil
|
||||
}
|
||||
|
||||
func waitRetryBackoff(ctx context.Context, attempt uint, callOpts *options) error {
|
||||
waitTime := time.Duration(0)
|
||||
if attempt > 0 {
|
||||
waitTime = callOpts.backoffFunc(attempt)
|
||||
}
|
||||
if waitTime > 0 {
|
||||
timer := time.NewTimer(waitTime)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return contextErrToGrpcErr(ctx.Err())
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSafeRetry returns "true", if request is safe for retry with the given error.
|
||||
func isSafeRetry(lg *zap.Logger, err error, callOpts *options) bool {
|
||||
if isContextError(err) {
|
||||
return false
|
||||
}
|
||||
switch callOpts.retryPolicy {
|
||||
case repeatable:
|
||||
return isSafeRetryImmutableRPC(err)
|
||||
case nonRepeatable:
|
||||
return isSafeRetryMutableRPC(err)
|
||||
default:
|
||||
lg.Warn("unrecognized retry policy", zap.String("retryPolicy", callOpts.retryPolicy.String()))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isContextError(err error) bool {
|
||||
return status.Code(err) == codes.DeadlineExceeded || status.Code(err) == codes.Canceled
|
||||
}
|
||||
|
||||
func contextErrToGrpcErr(err error) error {
|
||||
switch err {
|
||||
case context.DeadlineExceeded:
|
||||
return status.Errorf(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled:
|
||||
return status.Errorf(codes.Canceled, err.Error())
|
||||
default:
|
||||
return status.Errorf(codes.Unknown, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
defaultOptions = &options{
|
||||
retryPolicy: nonRepeatable,
|
||||
max: 0, // disable
|
||||
backoffFunc: backoffLinearWithJitter(50*time.Millisecond /*jitter*/, 0.10),
|
||||
retryAuth: true,
|
||||
}
|
||||
)
|
||||
|
||||
// backoffFunc denotes a family of functions that control the backoff duration between call retries.
|
||||
//
|
||||
// They are called with an identifier of the attempt, and should return a time the system client should
|
||||
// hold off for. If the time returned is longer than the `context.Context.Deadline` of the request
|
||||
// the deadline of the request takes precedence and the wait will be interrupted before proceeding
|
||||
// with the next iteration.
|
||||
type backoffFunc func(attempt uint) time.Duration
|
||||
|
||||
// withRetryPolicy sets the retry policy of this call.
|
||||
func withRetryPolicy(rp retryPolicy) retryOption {
|
||||
return retryOption{applyFunc: func(o *options) {
|
||||
o.retryPolicy = rp
|
||||
}}
|
||||
}
|
||||
|
||||
// withMax sets the maximum number of retries on this call, or this interceptor.
|
||||
func withMax(maxRetries uint) retryOption {
|
||||
return retryOption{applyFunc: func(o *options) {
|
||||
o.max = maxRetries
|
||||
}}
|
||||
}
|
||||
|
||||
// WithBackoff sets the `BackoffFunc `used to control time between retries.
|
||||
func withBackoff(bf backoffFunc) retryOption {
|
||||
return retryOption{applyFunc: func(o *options) {
|
||||
o.backoffFunc = bf
|
||||
}}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
retryPolicy retryPolicy
|
||||
max uint
|
||||
backoffFunc backoffFunc
|
||||
retryAuth bool
|
||||
}
|
||||
|
||||
// retryOption is a grpc.CallOption that is local to clientv3's retry interceptor.
|
||||
type retryOption struct {
|
||||
grpc.EmptyCallOption // make sure we implement private after() and before() fields so we don't panic.
|
||||
applyFunc func(opt *options)
|
||||
}
|
||||
|
||||
func reuseOrNewWithCallOptions(opt *options, retryOptions []retryOption) *options {
|
||||
if len(retryOptions) == 0 {
|
||||
return opt
|
||||
}
|
||||
optCopy := &options{}
|
||||
*optCopy = *opt
|
||||
for _, f := range retryOptions {
|
||||
f.applyFunc(optCopy)
|
||||
}
|
||||
return optCopy
|
||||
}
|
||||
|
||||
func filterCallOptions(callOptions []grpc.CallOption) (grpcOptions []grpc.CallOption, retryOptions []retryOption) {
|
||||
for _, opt := range callOptions {
|
||||
if co, ok := opt.(retryOption); ok {
|
||||
retryOptions = append(retryOptions, co)
|
||||
} else {
|
||||
grpcOptions = append(grpcOptions, opt)
|
||||
}
|
||||
}
|
||||
return grpcOptions, retryOptions
|
||||
}
|
||||
|
||||
// BackoffLinearWithJitter waits a set period of time, allowing for jitter (fractional adjustment).
|
||||
//
|
||||
// For example waitBetween=1s and jitter=0.10 can generate waits between 900ms and 1100ms.
|
||||
func backoffLinearWithJitter(waitBetween time.Duration, jitterFraction float64) backoffFunc {
|
||||
return func(attempt uint) time.Duration {
|
||||
return jitterUp(waitBetween, jitterFraction)
|
||||
}
|
||||
}
|
37
vendor/go.etcd.io/etcd/client/v3/sort.go
generated
vendored
Normal file
37
vendor/go.etcd.io/etcd/client/v3/sort.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
type SortTarget int
|
||||
type SortOrder int
|
||||
|
||||
const (
|
||||
SortNone SortOrder = iota
|
||||
SortAscend
|
||||
SortDescend
|
||||
)
|
||||
|
||||
const (
|
||||
SortByKey SortTarget = iota
|
||||
SortByVersion
|
||||
SortByCreateRevision
|
||||
SortByModRevision
|
||||
SortByValue
|
||||
)
|
||||
|
||||
type SortOption struct {
|
||||
Target SortTarget
|
||||
Order SortOrder
|
||||
}
|
151
vendor/go.etcd.io/etcd/client/v3/txn.go
generated
vendored
Normal file
151
vendor/go.etcd.io/etcd/client/v3/txn.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
// Copyright 2016 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Txn is the interface that wraps mini-transactions.
|
||||
//
|
||||
// Txn(context.TODO()).If(
|
||||
// Compare(Value(k1), ">", v1),
|
||||
// Compare(Version(k1), "=", 2)
|
||||
// ).Then(
|
||||
// OpPut(k2,v2), OpPut(k3,v3)
|
||||
// ).Else(
|
||||
// OpPut(k4,v4), OpPut(k5,v5)
|
||||
// ).Commit()
|
||||
//
|
||||
type Txn interface {
|
||||
// If takes a list of comparison. If all comparisons passed in succeed,
|
||||
// the operations passed into Then() will be executed. Or the operations
|
||||
// passed into Else() will be executed.
|
||||
If(cs ...Cmp) Txn
|
||||
|
||||
// Then takes a list of operations. The Ops list will be executed, if the
|
||||
// comparisons passed in If() succeed.
|
||||
Then(ops ...Op) Txn
|
||||
|
||||
// Else takes a list of operations. The Ops list will be executed, if the
|
||||
// comparisons passed in If() fail.
|
||||
Else(ops ...Op) Txn
|
||||
|
||||
// Commit tries to commit the transaction.
|
||||
Commit() (*TxnResponse, error)
|
||||
}
|
||||
|
||||
type txn struct {
|
||||
kv *kv
|
||||
ctx context.Context
|
||||
|
||||
mu sync.Mutex
|
||||
cif bool
|
||||
cthen bool
|
||||
celse bool
|
||||
|
||||
isWrite bool
|
||||
|
||||
cmps []*pb.Compare
|
||||
|
||||
sus []*pb.RequestOp
|
||||
fas []*pb.RequestOp
|
||||
|
||||
callOpts []grpc.CallOption
|
||||
}
|
||||
|
||||
func (txn *txn) If(cs ...Cmp) Txn {
|
||||
txn.mu.Lock()
|
||||
defer txn.mu.Unlock()
|
||||
|
||||
if txn.cif {
|
||||
panic("cannot call If twice!")
|
||||
}
|
||||
|
||||
if txn.cthen {
|
||||
panic("cannot call If after Then!")
|
||||
}
|
||||
|
||||
if txn.celse {
|
||||
panic("cannot call If after Else!")
|
||||
}
|
||||
|
||||
txn.cif = true
|
||||
|
||||
for i := range cs {
|
||||
txn.cmps = append(txn.cmps, (*pb.Compare)(&cs[i]))
|
||||
}
|
||||
|
||||
return txn
|
||||
}
|
||||
|
||||
func (txn *txn) Then(ops ...Op) Txn {
|
||||
txn.mu.Lock()
|
||||
defer txn.mu.Unlock()
|
||||
|
||||
if txn.cthen {
|
||||
panic("cannot call Then twice!")
|
||||
}
|
||||
if txn.celse {
|
||||
panic("cannot call Then after Else!")
|
||||
}
|
||||
|
||||
txn.cthen = true
|
||||
|
||||
for _, op := range ops {
|
||||
txn.isWrite = txn.isWrite || op.isWrite()
|
||||
txn.sus = append(txn.sus, op.toRequestOp())
|
||||
}
|
||||
|
||||
return txn
|
||||
}
|
||||
|
||||
func (txn *txn) Else(ops ...Op) Txn {
|
||||
txn.mu.Lock()
|
||||
defer txn.mu.Unlock()
|
||||
|
||||
if txn.celse {
|
||||
panic("cannot call Else twice!")
|
||||
}
|
||||
|
||||
txn.celse = true
|
||||
|
||||
for _, op := range ops {
|
||||
txn.isWrite = txn.isWrite || op.isWrite()
|
||||
txn.fas = append(txn.fas, op.toRequestOp())
|
||||
}
|
||||
|
||||
return txn
|
||||
}
|
||||
|
||||
func (txn *txn) Commit() (*TxnResponse, error) {
|
||||
txn.mu.Lock()
|
||||
defer txn.mu.Unlock()
|
||||
|
||||
r := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
|
||||
|
||||
var resp *pb.TxnResponse
|
||||
var err error
|
||||
resp, err = txn.kv.remote.Txn(txn.ctx, r, txn.callOpts...)
|
||||
if err != nil {
|
||||
return nil, toErr(txn.ctx, err)
|
||||
}
|
||||
return (*TxnResponse)(resp), nil
|
||||
}
|
31
vendor/go.etcd.io/etcd/client/v3/utils.go
generated
vendored
Normal file
31
vendor/go.etcd.io/etcd/client/v3/utils.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2018 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package clientv3
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// jitterUp adds random jitter to the duration.
|
||||
//
|
||||
// This adds or subtracts time from the duration within a given jitter fraction.
|
||||
// For example for 10s and jitter 0.1, it will return a time within [9s, 11s])
|
||||
//
|
||||
// Reference: https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware/util/backoffutils
|
||||
func jitterUp(duration time.Duration, jitter float64) time.Duration {
|
||||
multiplier := jitter * (rand.Float64()*2 - 1)
|
||||
return time.Duration(float64(duration) * (1 + multiplier))
|
||||
}
|
1042
vendor/go.etcd.io/etcd/client/v3/watch.go
generated
vendored
Normal file
1042
vendor/go.etcd.io/etcd/client/v3/watch.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user