mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
build: move e2e dependencies into e2e/go.mod
Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
15da101b1b
commit
bec6090996
202
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/LICENSE
generated
vendored
Normal file
202
e2e/vendor/go.etcd.io/etcd/client/pkg/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.
|
28
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_unix.go
generated
vendored
Normal file
28
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_unix.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package fileutil
|
||||
|
||||
import "os"
|
||||
|
||||
const (
|
||||
// PrivateDirMode grants owner to make/remove files inside the directory.
|
||||
PrivateDirMode = 0700
|
||||
)
|
||||
|
||||
// OpenDir opens a directory for syncing.
|
||||
func OpenDir(path string) (*os.File, error) { return os.Open(path) }
|
52
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_windows.go
generated
vendored
Normal file
52
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/dir_windows.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// PrivateDirMode grants owner to make/remove files inside the directory.
|
||||
PrivateDirMode = 0777
|
||||
)
|
||||
|
||||
// OpenDir opens a directory in windows with write access for syncing.
|
||||
func OpenDir(path string) (*os.File, error) {
|
||||
fd, err := openDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fd), path), nil
|
||||
}
|
||||
|
||||
func openDir(path string) (fd syscall.Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE)
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
|
||||
createmode := uint32(syscall.OPEN_EXISTING)
|
||||
fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0)
|
||||
}
|
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/doc.go
generated
vendored
Normal file
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/doc.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// 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 fileutil implements utility functions related to files and paths.
|
||||
package fileutil
|
60
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/filereader.go
generated
vendored
Normal file
60
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/filereader.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2022 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 fileutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// FileReader is a wrapper of io.Reader. It also provides file info.
|
||||
type FileReader interface {
|
||||
io.Reader
|
||||
FileInfo() (fs.FileInfo, error)
|
||||
}
|
||||
|
||||
type fileReader struct {
|
||||
*os.File
|
||||
}
|
||||
|
||||
func NewFileReader(f *os.File) FileReader {
|
||||
return &fileReader{f}
|
||||
}
|
||||
|
||||
func (fr *fileReader) FileInfo() (fs.FileInfo, error) {
|
||||
return fr.Stat()
|
||||
}
|
||||
|
||||
// FileBufReader is a wrapper of bufio.Reader. It also provides file info.
|
||||
type FileBufReader struct {
|
||||
*bufio.Reader
|
||||
fi fs.FileInfo
|
||||
}
|
||||
|
||||
func NewFileBufReader(fr FileReader) *FileBufReader {
|
||||
bufReader := bufio.NewReader(fr)
|
||||
fi, err := fr.FileInfo()
|
||||
if err != nil {
|
||||
// This should never happen.
|
||||
panic(err)
|
||||
}
|
||||
return &FileBufReader{bufReader, fi}
|
||||
}
|
||||
|
||||
func (fbr *FileBufReader) FileInfo() fs.FileInfo {
|
||||
return fbr.fi
|
||||
}
|
168
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/fileutil.go
generated
vendored
Normal file
168
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/fileutil.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
// PrivateFileMode grants owner to read/write a file.
|
||||
PrivateFileMode = 0600
|
||||
)
|
||||
|
||||
// IsDirWriteable checks if dir is writable by writing and removing a file
|
||||
// to dir. It returns nil if dir is writable.
|
||||
func IsDirWriteable(dir string) error {
|
||||
f, err := filepath.Abs(filepath.Join(dir, ".touch"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Remove(f)
|
||||
}
|
||||
|
||||
// TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
|
||||
// does not exists. TouchDirAll also ensures the given directory is writable.
|
||||
func TouchDirAll(lg *zap.Logger, dir string) error {
|
||||
// If path is already a directory, MkdirAll does nothing and returns nil, so,
|
||||
// first check if dir exist with an expected permission mode.
|
||||
if Exist(dir) {
|
||||
err := CheckDirPermission(dir, PrivateDirMode)
|
||||
if err != nil {
|
||||
lg.Warn("check file permission", zap.Error(err))
|
||||
}
|
||||
} else {
|
||||
err := os.MkdirAll(dir, PrivateDirMode)
|
||||
if err != nil {
|
||||
// if mkdirAll("a/text") and "text" is not
|
||||
// a directory, this will return syscall.ENOTDIR
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return IsDirWriteable(dir)
|
||||
}
|
||||
|
||||
// CreateDirAll is similar to TouchDirAll but returns error
|
||||
// if the deepest directory was not empty.
|
||||
func CreateDirAll(lg *zap.Logger, dir string) error {
|
||||
err := TouchDirAll(lg, dir)
|
||||
if err == nil {
|
||||
var ns []string
|
||||
ns, err = ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ns) != 0 {
|
||||
err = fmt.Errorf("expected %q to be empty, got %q", dir, ns)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Exist returns true if a file or directory exists.
|
||||
func Exist(name string) bool {
|
||||
_, err := os.Stat(name)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// DirEmpty returns true if a directory empty and can access.
|
||||
func DirEmpty(name string) bool {
|
||||
ns, err := ReadDir(name)
|
||||
return len(ns) == 0 && err == nil
|
||||
}
|
||||
|
||||
// ZeroToEnd zeros a file starting from SEEK_CUR to its SEEK_END. May temporarily
|
||||
// shorten the length of the file.
|
||||
func ZeroToEnd(f *os.File) error {
|
||||
// TODO: support FALLOC_FL_ZERO_RANGE
|
||||
off, err := f.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lenf, lerr := f.Seek(0, io.SeekEnd)
|
||||
if lerr != nil {
|
||||
return lerr
|
||||
}
|
||||
if err = f.Truncate(off); err != nil {
|
||||
return err
|
||||
}
|
||||
// make sure blocks remain allocated
|
||||
if err = Preallocate(f, lenf, true); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Seek(off, io.SeekStart)
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckDirPermission checks permission on an existing dir.
|
||||
// Returns error if dir is empty or exist with a different permission than specified.
|
||||
func CheckDirPermission(dir string, perm os.FileMode) error {
|
||||
if !Exist(dir) {
|
||||
return fmt.Errorf("directory %q empty, cannot check permission", dir)
|
||||
}
|
||||
//check the existing permission on the directory
|
||||
dirInfo, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dirMode := dirInfo.Mode().Perm()
|
||||
if dirMode != perm {
|
||||
err = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data", dir, dirInfo.Mode(), os.FileMode(PrivateDirMode))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveMatchFile deletes file if matchFunc is true on an existing dir
|
||||
// Returns error if the dir does not exist or remove file fail
|
||||
func RemoveMatchFile(lg *zap.Logger, dir string, matchFunc func(fileName string) bool) error {
|
||||
if lg == nil {
|
||||
lg = zap.NewNop()
|
||||
}
|
||||
if !Exist(dir) {
|
||||
return fmt.Errorf("directory %s does not exist", dir)
|
||||
}
|
||||
fileNames, err := ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var removeFailedFiles []string
|
||||
for _, fileName := range fileNames {
|
||||
if matchFunc(fileName) {
|
||||
file := filepath.Join(dir, fileName)
|
||||
if err = os.Remove(file); err != nil {
|
||||
removeFailedFiles = append(removeFailedFiles, fileName)
|
||||
lg.Error("remove file failed",
|
||||
zap.String("file", file),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(removeFailedFiles) != 0 {
|
||||
return fmt.Errorf("remove file(s) %v error", removeFailedFiles)
|
||||
}
|
||||
return nil
|
||||
}
|
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock.go
generated
vendored
Normal file
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLocked = errors.New("fileutil: file already locked")
|
||||
)
|
||||
|
||||
type LockedFile struct{ *os.File }
|
50
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_flock.go
generated
vendored
Normal file
50
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_flock.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows && !plan9 && !solaris
|
||||
// +build !windows,!plan9,!solaris
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func flockTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
|
||||
f.Close()
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
err = ErrLocked
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func flockLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, err
|
||||
}
|
93
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_linux.go
generated
vendored
Normal file
93
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_linux.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// This used to call syscall.Flock() but that call fails with EBADF on NFS.
|
||||
// An alternative is lockf() which works on NFS but that call lets a process lock
|
||||
// the same file twice. Instead, use Linux's non-standard open file descriptor
|
||||
// locks which will block if the process already holds the file lock.
|
||||
|
||||
var (
|
||||
wrlck = syscall.Flock_t{
|
||||
Type: syscall.F_WRLCK,
|
||||
Whence: int16(io.SeekStart),
|
||||
Start: 0,
|
||||
Len: 0,
|
||||
}
|
||||
|
||||
linuxTryLockFile = flockTryLockFile
|
||||
linuxLockFile = flockLockFile
|
||||
)
|
||||
|
||||
func init() {
|
||||
// use open file descriptor locks if the system supports it
|
||||
getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
|
||||
if err := syscall.FcntlFlock(0, unix.F_OFD_GETLK, &getlk); err == nil {
|
||||
linuxTryLockFile = ofdTryLockFile
|
||||
linuxLockFile = ofdLockFile
|
||||
}
|
||||
}
|
||||
|
||||
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
return linuxTryLockFile(path, flag, perm)
|
||||
}
|
||||
|
||||
func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%v)", path, err)
|
||||
}
|
||||
|
||||
flock := wrlck
|
||||
if err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLK, &flock); err != nil {
|
||||
f.Close()
|
||||
if err == syscall.EWOULDBLOCK {
|
||||
err = ErrLocked
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
return linuxLockFile(path, flag, perm)
|
||||
}
|
||||
|
||||
func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ofdLockFile failed to open %q (%v)", path, err)
|
||||
}
|
||||
|
||||
flock := wrlck
|
||||
err = syscall.FcntlFlock(f.Fd(), unix.F_OFD_SETLKW, &flock)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_plan9.go
generated
vendored
Normal file
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_plan9.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
if err := os.Chmod(path, syscall.DMEXCL|PrivateFileMode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, ErrLocked
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
if err := os.Chmod(path, syscall.DMEXCL|PrivateFileMode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err == nil {
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
63
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_solaris.go
generated
vendored
Normal file
63
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_solaris.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// 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.
|
||||
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
var lock syscall.Flock_t
|
||||
lock.Start = 0
|
||||
lock.Len = 0
|
||||
lock.Pid = 0
|
||||
lock.Type = syscall.F_WRLCK
|
||||
lock.Whence = 0
|
||||
lock.Pid = 0
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &lock); err != nil {
|
||||
f.Close()
|
||||
if err == syscall.EAGAIN {
|
||||
err = ErrLocked
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
var lock syscall.Flock_t
|
||||
lock.Start = 0
|
||||
lock.Len = 0
|
||||
lock.Pid = 0
|
||||
lock.Type = syscall.F_WRLCK
|
||||
lock.Whence = 0
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLKW, &lock); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_unix.go
generated
vendored
Normal file
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_unix.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows && !plan9 && !solaris && !linux
|
||||
// +build !windows,!plan9,!solaris,!linux
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
return flockTryLockFile(path, flag, perm)
|
||||
}
|
||||
|
||||
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
return flockLockFile(path, flag, perm)
|
||||
}
|
126
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_windows.go
generated
vendored
Normal file
126
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_windows.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procLockFileEx = modkernel32.NewProc("LockFileEx")
|
||||
|
||||
errLocked = errors.New("the process cannot access the file because another process has locked a portion of the file")
|
||||
)
|
||||
|
||||
const (
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
|
||||
LOCKFILE_EXCLUSIVE_LOCK = 2
|
||||
LOCKFILE_FAIL_IMMEDIATELY = 1
|
||||
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
|
||||
errLockViolation syscall.Errno = 0x21
|
||||
)
|
||||
|
||||
func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := open(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := lockFile(syscall.Handle(f.Fd()), LOCKFILE_FAIL_IMMEDIATELY); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
|
||||
f, err := open(path, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := lockFile(syscall.Handle(f.Fd()), 0); err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &LockedFile{f}, nil
|
||||
}
|
||||
|
||||
func open(path string, flag int, perm os.FileMode) (*os.File, error) {
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("cannot open empty filename")
|
||||
}
|
||||
var access uint32
|
||||
switch flag {
|
||||
case syscall.O_RDONLY:
|
||||
access = syscall.GENERIC_READ
|
||||
case syscall.O_WRONLY:
|
||||
access = syscall.GENERIC_WRITE
|
||||
case syscall.O_RDWR:
|
||||
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
|
||||
case syscall.O_WRONLY | syscall.O_CREAT:
|
||||
access = syscall.GENERIC_ALL
|
||||
default:
|
||||
panic(fmt.Errorf("flag %v is not supported", flag))
|
||||
}
|
||||
fd, err := syscall.CreateFile(&(syscall.StringToUTF16(path)[0]),
|
||||
access,
|
||||
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
|
||||
nil,
|
||||
syscall.OPEN_ALWAYS,
|
||||
syscall.FILE_ATTRIBUTE_NORMAL,
|
||||
0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fd), path), nil
|
||||
}
|
||||
|
||||
func lockFile(fd syscall.Handle, flags uint32) error {
|
||||
var flag uint32 = LOCKFILE_EXCLUSIVE_LOCK
|
||||
flag |= flags
|
||||
if fd == syscall.InvalidHandle {
|
||||
return nil
|
||||
}
|
||||
err := lockFileEx(fd, flag, 1, 0, &syscall.Overlapped{})
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if err.Error() == errLocked.Error() {
|
||||
return ErrLocked
|
||||
} else if err != errLockViolation {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lockFileEx(h syscall.Handle, flags, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
|
||||
var reserved uint32 = 0
|
||||
r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
54
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate.go
generated
vendored
Normal file
54
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Preallocate tries to allocate the space for given
|
||||
// file. This operation is only supported on linux by a
|
||||
// few filesystems (btrfs, ext4, etc.).
|
||||
// If the operation is unsupported, no error will be returned.
|
||||
// Otherwise, the error encountered will be returned.
|
||||
func Preallocate(f *os.File, sizeInBytes int64, extendFile bool) error {
|
||||
if sizeInBytes == 0 {
|
||||
// fallocate will return EINVAL if length is 0; skip
|
||||
return nil
|
||||
}
|
||||
if extendFile {
|
||||
return preallocExtend(f, sizeInBytes)
|
||||
}
|
||||
return preallocFixed(f, sizeInBytes)
|
||||
}
|
||||
|
||||
func preallocExtendTrunc(f *os.File, sizeInBytes int64) error {
|
||||
curOff, err := f.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size, err := f.Seek(sizeInBytes, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = f.Seek(curOff, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
if sizeInBytes > size {
|
||||
return nil
|
||||
}
|
||||
return f.Truncate(sizeInBytes)
|
||||
}
|
67
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_darwin.go
generated
vendored
Normal file
67
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_darwin.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func preallocExtend(f *os.File, sizeInBytes int64) error {
|
||||
if err := preallocFixed(f, sizeInBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
return preallocExtendTrunc(f, sizeInBytes)
|
||||
}
|
||||
|
||||
func preallocFixed(f *os.File, sizeInBytes int64) error {
|
||||
// allocate all requested space or no space at all
|
||||
// TODO: allocate contiguous space on disk with F_ALLOCATECONTIG flag
|
||||
fstore := &unix.Fstore_t{
|
||||
Flags: unix.F_ALLOCATEALL,
|
||||
Posmode: unix.F_PEOFPOSMODE,
|
||||
Length: sizeInBytes,
|
||||
}
|
||||
err := unix.FcntlFstore(f.Fd(), unix.F_PREALLOCATE, fstore)
|
||||
if err == nil || err == unix.ENOTSUP {
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrong argument to fallocate syscall
|
||||
if err == unix.EINVAL {
|
||||
// filesystem "st_blocks" are allocated in the units of
|
||||
// "Allocation Block Size" (run "diskutil info /" command)
|
||||
var stat syscall.Stat_t
|
||||
syscall.Fstat(int(f.Fd()), &stat)
|
||||
|
||||
// syscall.Statfs_t.Bsize is "optimal transfer block size"
|
||||
// and contains matching 4096 value when latest OS X kernel
|
||||
// supports 4,096 KB filesystem block size
|
||||
var statfs syscall.Statfs_t
|
||||
syscall.Fstatfs(int(f.Fd()), &statfs)
|
||||
blockSize := int64(statfs.Bsize)
|
||||
|
||||
if stat.Blocks*blockSize >= sizeInBytes {
|
||||
// enough blocks are already allocated
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
50
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unix.go
generated
vendored
Normal file
50
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unix.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// 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.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func preallocExtend(f *os.File, sizeInBytes int64) error {
|
||||
// use mode = 0 to change size
|
||||
err := syscall.Fallocate(int(f.Fd()), 0, 0, sizeInBytes)
|
||||
if err != nil {
|
||||
errno, ok := err.(syscall.Errno)
|
||||
// not supported; fallback
|
||||
// fallocate EINTRs frequently in some environments; fallback
|
||||
if ok && (errno == syscall.ENOTSUP || errno == syscall.EINTR) {
|
||||
return preallocExtendTrunc(f, sizeInBytes)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func preallocFixed(f *os.File, sizeInBytes int64) error {
|
||||
// use mode = 1 to keep size; see FALLOC_FL_KEEP_SIZE
|
||||
err := syscall.Fallocate(int(f.Fd()), 1, 0, sizeInBytes)
|
||||
if err != nil {
|
||||
errno, ok := err.(syscall.Errno)
|
||||
// treat not supported as nil error
|
||||
if ok && errno == syscall.ENOTSUP {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unsupported.go
generated
vendored
Normal file
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/preallocate_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
|
||||
package fileutil
|
||||
|
||||
import "os"
|
||||
|
||||
func preallocExtend(f *os.File, sizeInBytes int64) error {
|
||||
return preallocExtendTrunc(f, sizeInBytes)
|
||||
}
|
||||
|
||||
func preallocFixed(f *os.File, sizeInBytes int64) error { return nil }
|
112
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/purge.go
generated
vendored
Normal file
112
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/purge.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func PurgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
|
||||
return purgeFile(lg, dirname, suffix, max, interval, stop, nil, nil, true)
|
||||
}
|
||||
|
||||
func PurgeFileWithDoneNotify(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) {
|
||||
doneC := make(chan struct{})
|
||||
errC := purgeFile(lg, dirname, suffix, max, interval, stop, nil, doneC, true)
|
||||
return doneC, errC
|
||||
}
|
||||
|
||||
func PurgeFileWithoutFlock(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) {
|
||||
doneC := make(chan struct{})
|
||||
errC := purgeFile(lg, dirname, suffix, max, interval, stop, nil, doneC, false)
|
||||
return doneC, errC
|
||||
}
|
||||
|
||||
// purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil.
|
||||
// if donec is non-nil, the function closes it to notify its exit.
|
||||
func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string, donec chan<- struct{}, flock bool) <-chan error {
|
||||
if lg == nil {
|
||||
lg = zap.NewNop()
|
||||
}
|
||||
errC := make(chan error, 1)
|
||||
lg.Info("started to purge file",
|
||||
zap.String("dir", dirname),
|
||||
zap.String("suffix", suffix),
|
||||
zap.Uint("max", max),
|
||||
zap.Duration("interval", interval))
|
||||
|
||||
go func() {
|
||||
if donec != nil {
|
||||
defer close(donec)
|
||||
}
|
||||
for {
|
||||
fnames, err := ReadDir(dirname)
|
||||
if err != nil {
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
newfnames := make([]string, 0)
|
||||
for _, fname := range fnames {
|
||||
if strings.HasSuffix(fname, suffix) {
|
||||
newfnames = append(newfnames, fname)
|
||||
}
|
||||
}
|
||||
sort.Strings(newfnames)
|
||||
fnames = newfnames
|
||||
for len(newfnames) > int(max) {
|
||||
f := filepath.Join(dirname, newfnames[0])
|
||||
var l *LockedFile
|
||||
if flock {
|
||||
l, err = TryLockFile(f, os.O_WRONLY, PrivateFileMode)
|
||||
if err != nil {
|
||||
lg.Warn("failed to lock file", zap.String("path", f), zap.Error(err))
|
||||
break
|
||||
}
|
||||
}
|
||||
if err = os.Remove(f); err != nil {
|
||||
lg.Error("failed to remove file", zap.String("path", f), zap.Error(err))
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
if flock {
|
||||
if err = l.Close(); err != nil {
|
||||
lg.Error("failed to unlock/close", zap.String("path", l.Name()), zap.Error(err))
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
lg.Info("purged", zap.String("path", f))
|
||||
newfnames = newfnames[1:]
|
||||
}
|
||||
if purgec != nil {
|
||||
for i := 0; i < len(fnames)-len(newfnames); i++ {
|
||||
purgec <- fnames[i]
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-time.After(interval):
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return errC
|
||||
}
|
70
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/read_dir.go
generated
vendored
Normal file
70
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/read_dir.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
// 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 fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ReadDirOp represents an read-directory operation.
|
||||
type ReadDirOp struct {
|
||||
ext string
|
||||
}
|
||||
|
||||
// ReadDirOption configures archiver operations.
|
||||
type ReadDirOption func(*ReadDirOp)
|
||||
|
||||
// WithExt filters file names by their extensions.
|
||||
// (e.g. WithExt(".wal") to list only WAL files)
|
||||
func WithExt(ext string) ReadDirOption {
|
||||
return func(op *ReadDirOp) { op.ext = ext }
|
||||
}
|
||||
|
||||
func (op *ReadDirOp) applyOpts(opts []ReadDirOption) {
|
||||
for _, opt := range opts {
|
||||
opt(op)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadDir returns the filenames in the given directory in sorted order.
|
||||
func ReadDir(d string, opts ...ReadDirOption) ([]string, error) {
|
||||
op := &ReadDirOp{}
|
||||
op.applyOpts(opts)
|
||||
|
||||
dir, err := os.Open(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
names, err := dir.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
if op.ext != "" {
|
||||
tss := make([]string, 0)
|
||||
for _, v := range names {
|
||||
if filepath.Ext(v) == op.ext {
|
||||
tss = append(tss, v)
|
||||
}
|
||||
}
|
||||
names = tss
|
||||
}
|
||||
return names, nil
|
||||
}
|
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync.go
generated
vendored
Normal file
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
|
||||
package fileutil
|
||||
|
||||
import "os"
|
||||
|
||||
// Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
|
||||
func Fsync(f *os.File) error {
|
||||
return f.Sync()
|
||||
}
|
||||
|
||||
// Fdatasync is a wrapper around file.Sync(). Special handling is needed on linux platform.
|
||||
func Fdatasync(f *os.File) error {
|
||||
return f.Sync()
|
||||
}
|
39
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_darwin.go
generated
vendored
Normal file
39
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_darwin.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// 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.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Fsync on HFS/OSX flushes the data on to the physical drive but the drive
|
||||
// may not write it to the persistent media for quite sometime and it may be
|
||||
// written in out-of-order sequence. Using F_FULLFSYNC ensures that the
|
||||
// physical drive's buffer will also get flushed to the media.
|
||||
func Fsync(f *os.File) error {
|
||||
_, err := unix.FcntlInt(f.Fd(), unix.F_FULLFSYNC, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// Fdatasync on darwin platform invokes fcntl(F_FULLFSYNC) for actual persistence
|
||||
// on physical drive media.
|
||||
func Fdatasync(f *os.File) error {
|
||||
return Fsync(f)
|
||||
}
|
35
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_linux.go
generated
vendored
Normal file
35
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/fileutil/sync_linux.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Fsync is a wrapper around file.Sync(). Special handling is needed on darwin platform.
|
||||
func Fsync(f *os.File) error {
|
||||
return f.Sync()
|
||||
}
|
||||
|
||||
// Fdatasync is similar to fsync(), but does not flush modified metadata
|
||||
// unless that metadata is needed in order to allow a subsequent data retrieval
|
||||
// to be correctly handled.
|
||||
func Fdatasync(f *os.File) error {
|
||||
return syscall.Fdatasync(int(f.Fd()))
|
||||
}
|
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/doc.go
generated
vendored
Normal file
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/doc.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// 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 logutil includes utilities to facilitate logging.
|
||||
package logutil
|
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/log_level.go
generated
vendored
Normal file
30
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/log_level.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 logutil
|
||||
|
||||
import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var DefaultLogLevel = "info"
|
||||
|
||||
// ConvertToZapLevel converts log level string to zapcore.Level.
|
||||
func ConvertToZapLevel(lvl string) zapcore.Level {
|
||||
var level zapcore.Level
|
||||
if err := level.Set(lvl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return level
|
||||
}
|
108
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go
generated
vendored
Normal file
108
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// 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 logutil
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// CreateDefaultZapLogger creates a logger with default zap configuration
|
||||
func CreateDefaultZapLogger(level zapcore.Level) (*zap.Logger, error) {
|
||||
lcfg := DefaultZapLoggerConfig
|
||||
lcfg.Level = zap.NewAtomicLevelAt(level)
|
||||
c, err := lcfg.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DefaultZapLoggerConfig defines default zap logger configuration.
|
||||
var DefaultZapLoggerConfig = zap.Config{
|
||||
Level: zap.NewAtomicLevelAt(ConvertToZapLevel(DefaultLogLevel)),
|
||||
|
||||
Development: false,
|
||||
Sampling: &zap.SamplingConfig{
|
||||
Initial: 100,
|
||||
Thereafter: 100,
|
||||
},
|
||||
|
||||
Encoding: "json",
|
||||
|
||||
// copied from "zap.NewProductionEncoderConfig" with some updates
|
||||
EncoderConfig: zapcore.EncoderConfig{
|
||||
TimeKey: "ts",
|
||||
LevelKey: "level",
|
||||
NameKey: "logger",
|
||||
CallerKey: "caller",
|
||||
MessageKey: "msg",
|
||||
StacktraceKey: "stacktrace",
|
||||
LineEnding: zapcore.DefaultLineEnding,
|
||||
EncodeLevel: zapcore.LowercaseLevelEncoder,
|
||||
|
||||
// Custom EncodeTime function to ensure we match format and precision of historic capnslog timestamps
|
||||
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString(t.Format("2006-01-02T15:04:05.000000Z0700"))
|
||||
},
|
||||
|
||||
EncodeDuration: zapcore.StringDurationEncoder,
|
||||
EncodeCaller: zapcore.ShortCallerEncoder,
|
||||
},
|
||||
|
||||
// Use "/dev/null" to discard all
|
||||
OutputPaths: []string{"stderr"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
|
||||
// MergeOutputPaths merges logging output paths, resolving conflicts.
|
||||
func MergeOutputPaths(cfg zap.Config) zap.Config {
|
||||
outputs := make(map[string]struct{})
|
||||
for _, v := range cfg.OutputPaths {
|
||||
outputs[v] = struct{}{}
|
||||
}
|
||||
outputSlice := make([]string, 0)
|
||||
if _, ok := outputs["/dev/null"]; ok {
|
||||
// "/dev/null" to discard all
|
||||
outputSlice = []string{"/dev/null"}
|
||||
} else {
|
||||
for k := range outputs {
|
||||
outputSlice = append(outputSlice, k)
|
||||
}
|
||||
}
|
||||
cfg.OutputPaths = outputSlice
|
||||
sort.Strings(cfg.OutputPaths)
|
||||
|
||||
errOutputs := make(map[string]struct{})
|
||||
for _, v := range cfg.ErrorOutputPaths {
|
||||
errOutputs[v] = struct{}{}
|
||||
}
|
||||
errOutputSlice := make([]string, 0)
|
||||
if _, ok := errOutputs["/dev/null"]; ok {
|
||||
// "/dev/null" to discard all
|
||||
errOutputSlice = []string{"/dev/null"}
|
||||
} else {
|
||||
for k := range errOutputs {
|
||||
errOutputSlice = append(errOutputSlice, k)
|
||||
}
|
||||
}
|
||||
cfg.ErrorOutputPaths = errOutputSlice
|
||||
sort.Strings(cfg.ErrorOutputPaths)
|
||||
|
||||
return cfg
|
||||
}
|
93
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap_journal.go
generated
vendored
Normal file
93
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/logutil/zap_journal.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package logutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"go.etcd.io/etcd/client/pkg/v3/systemd"
|
||||
|
||||
"github.com/coreos/go-systemd/v22/journal"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// NewJournalWriter wraps "io.Writer" to redirect log output
|
||||
// to the local systemd journal. If journald send fails, it fails
|
||||
// back to writing to the original writer.
|
||||
// The decode overhead is only <30µs per write.
|
||||
// Reference: https://github.com/coreos/pkg/blob/master/capnslog/journald_formatter.go
|
||||
func NewJournalWriter(wr io.Writer) (io.Writer, error) {
|
||||
return &journalWriter{Writer: wr}, systemd.DialJournal()
|
||||
}
|
||||
|
||||
type journalWriter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// WARN: assume that etcd uses default field names in zap encoder config
|
||||
// make sure to keep this up-to-date!
|
||||
type logLine struct {
|
||||
Level string `json:"level"`
|
||||
Caller string `json:"caller"`
|
||||
}
|
||||
|
||||
func (w *journalWriter) Write(p []byte) (int, error) {
|
||||
line := &logLine{}
|
||||
if err := json.NewDecoder(bytes.NewReader(p)).Decode(line); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var pri journal.Priority
|
||||
switch line.Level {
|
||||
case zapcore.DebugLevel.String():
|
||||
pri = journal.PriDebug
|
||||
case zapcore.InfoLevel.String():
|
||||
pri = journal.PriInfo
|
||||
|
||||
case zapcore.WarnLevel.String():
|
||||
pri = journal.PriWarning
|
||||
case zapcore.ErrorLevel.String():
|
||||
pri = journal.PriErr
|
||||
|
||||
case zapcore.DPanicLevel.String():
|
||||
pri = journal.PriCrit
|
||||
case zapcore.PanicLevel.String():
|
||||
pri = journal.PriCrit
|
||||
case zapcore.FatalLevel.String():
|
||||
pri = journal.PriCrit
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unknown log level: %q", line.Level))
|
||||
}
|
||||
|
||||
err := journal.Send(string(p), pri, map[string]string{
|
||||
"PACKAGE": filepath.Dir(line.Caller),
|
||||
"SYSLOG_IDENTIFIER": filepath.Base(os.Args[0]),
|
||||
})
|
||||
if err != nil {
|
||||
// "journal" also falls back to stderr
|
||||
// "fmt.Fprintln(os.Stderr, s)"
|
||||
return w.Writer.Write(p)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/systemd/doc.go
generated
vendored
Normal file
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/systemd/doc.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// 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 systemd provides utility functions for systemd.
|
||||
package systemd
|
29
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/systemd/journal.go
generated
vendored
Normal file
29
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/systemd/journal.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 systemd
|
||||
|
||||
import "net"
|
||||
|
||||
// DialJournal returns no error if the process can dial journal socket.
|
||||
// Returns an error if dial failed, which indicates journald is not available
|
||||
// (e.g. run embedded etcd as docker daemon).
|
||||
// Reference: https://github.com/coreos/go-systemd/blob/master/journal/journal.go.
|
||||
func DialJournal() error {
|
||||
conn, err := net.Dial("unixgram", "/run/systemd/journal/socket")
|
||||
if conn != nil {
|
||||
defer conn.Close()
|
||||
}
|
||||
return err
|
||||
}
|
56
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/cipher_suites.go
generated
vendored
Normal file
56
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/cipher_suites.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
// 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 tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetCipherSuite returns the corresponding cipher suite,
|
||||
// and boolean value if it is supported.
|
||||
func GetCipherSuite(s string) (uint16, bool) {
|
||||
for _, c := range tls.CipherSuites() {
|
||||
if s == c.Name {
|
||||
return c.ID, true
|
||||
}
|
||||
}
|
||||
for _, c := range tls.InsecureCipherSuites() {
|
||||
if s == c.Name {
|
||||
return c.ID, true
|
||||
}
|
||||
}
|
||||
switch s {
|
||||
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
|
||||
return tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, true
|
||||
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
|
||||
return tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetCipherSuites returns list of corresponding cipher suite IDs.
|
||||
func GetCipherSuites(ss []string) ([]uint16, error) {
|
||||
cs := make([]uint16, len(ss))
|
||||
for i, s := range ss {
|
||||
var ok bool
|
||||
cs[i], ok = GetCipherSuite(s)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected TLS cipher suite %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/doc.go
generated
vendored
Normal file
16
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/doc.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// 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 tlsutil provides utility functions for handling TLS.
|
||||
package tlsutil
|
73
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/tlsutil.go
generated
vendored
Normal file
73
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/tlsutil.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
// 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 tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// NewCertPool creates x509 certPool with provided CA files.
|
||||
func NewCertPool(CAFiles []string) (*x509.CertPool, error) {
|
||||
certPool := x509.NewCertPool()
|
||||
|
||||
for _, CAFile := range CAFiles {
|
||||
pemByte, err := ioutil.ReadFile(CAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
var block *pem.Block
|
||||
block, pemByte = pem.Decode(pemByte)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certPool.AddCert(cert)
|
||||
}
|
||||
}
|
||||
|
||||
return certPool, nil
|
||||
}
|
||||
|
||||
// NewCert generates TLS cert by using the given cert,key and parse function.
|
||||
func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certificate, error)) (*tls.Certificate, error) {
|
||||
cert, err := ioutil.ReadFile(certfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := ioutil.ReadFile(keyfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if parseFunc == nil {
|
||||
parseFunc = tls.X509KeyPair
|
||||
}
|
||||
|
||||
tlsCert, err := parseFunc(cert, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tlsCert, nil
|
||||
}
|
47
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/versions.go
generated
vendored
Normal file
47
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/tlsutil/versions.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2023 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tlsutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TLSVersion string
|
||||
|
||||
// Constants for TLS versions.
|
||||
const (
|
||||
TLSVersionDefault TLSVersion = ""
|
||||
TLSVersion12 TLSVersion = "TLS1.2"
|
||||
TLSVersion13 TLSVersion = "TLS1.3"
|
||||
)
|
||||
|
||||
// GetTLSVersion returns the corresponding tls.Version or error.
|
||||
func GetTLSVersion(version string) (uint16, error) {
|
||||
var v uint16
|
||||
|
||||
switch version {
|
||||
case string(TLSVersionDefault):
|
||||
v = 0 // 0 means let Go decide.
|
||||
case string(TLSVersion12):
|
||||
v = tls.VersionTLS12
|
||||
case string(TLSVersion13):
|
||||
v = tls.VersionTLS13
|
||||
default:
|
||||
return 0, fmt.Errorf("unexpected TLS version %q (must be one of: TLS1.2, TLS1.3)", version)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
17
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/doc.go
generated
vendored
Normal file
17
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/doc.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 transport implements various HTTP transport utilities based on Go
|
||||
// net package.
|
||||
package transport
|
116
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener.go
generated
vendored
Normal file
116
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewKeepAliveListener returns a listener that listens on the given address.
|
||||
// Be careful when wrap around KeepAliveListener with another Listener if TLSInfo is not nil.
|
||||
// Some pkgs (like go/http) might expect Listener to return TLSConn type to start TLS handshake.
|
||||
// http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
|
||||
//
|
||||
// Note(ahrtr):
|
||||
// only `net.TCPConn` supports `SetKeepAlive` and `SetKeepAlivePeriod`
|
||||
// by default, so if you want to wrap multiple layers of net.Listener,
|
||||
// the `keepaliveListener` should be the one which is closest to the
|
||||
// original `net.Listener` implementation, namely `TCPListener`.
|
||||
func NewKeepAliveListener(l net.Listener, scheme string, tlscfg *tls.Config) (net.Listener, error) {
|
||||
kal := &keepaliveListener{
|
||||
Listener: l,
|
||||
}
|
||||
|
||||
if scheme == "https" {
|
||||
if tlscfg == nil {
|
||||
return nil, fmt.Errorf("cannot listen on TLS for given listener: KeyFile and CertFile are not presented")
|
||||
}
|
||||
return newTLSKeepaliveListener(kal, tlscfg), nil
|
||||
}
|
||||
|
||||
return kal, nil
|
||||
}
|
||||
|
||||
type keepaliveListener struct{ net.Listener }
|
||||
|
||||
func (kln *keepaliveListener) Accept() (net.Conn, error) {
|
||||
c, err := kln.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kac, err := createKeepaliveConn(c)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create keepalive connection failed, %w", err)
|
||||
}
|
||||
// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
|
||||
// default on linux: 30 + 8 * 30
|
||||
// default on osx: 30 + 8 * 75
|
||||
if err := kac.SetKeepAlive(true); err != nil {
|
||||
return nil, fmt.Errorf("SetKeepAlive failed, %w", err)
|
||||
}
|
||||
if err := kac.SetKeepAlivePeriod(30 * time.Second); err != nil {
|
||||
return nil, fmt.Errorf("SetKeepAlivePeriod failed, %w", err)
|
||||
}
|
||||
return kac, nil
|
||||
}
|
||||
|
||||
func createKeepaliveConn(c net.Conn) (*keepAliveConn, error) {
|
||||
tcpc, ok := c.(*net.TCPConn)
|
||||
if !ok {
|
||||
return nil, ErrNotTCP
|
||||
}
|
||||
return &keepAliveConn{tcpc}, nil
|
||||
}
|
||||
|
||||
type keepAliveConn struct {
|
||||
*net.TCPConn
|
||||
}
|
||||
|
||||
// SetKeepAlive sets keepalive
|
||||
func (l *keepAliveConn) SetKeepAlive(doKeepAlive bool) error {
|
||||
return l.TCPConn.SetKeepAlive(doKeepAlive)
|
||||
}
|
||||
|
||||
// A tlsKeepaliveListener implements a network listener (net.Listener) for TLS connections.
|
||||
type tlsKeepaliveListener struct {
|
||||
net.Listener
|
||||
config *tls.Config
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next incoming TLS connection.
|
||||
// The returned connection c is a *tls.Conn.
|
||||
func (l *tlsKeepaliveListener) Accept() (c net.Conn, err error) {
|
||||
c, err = l.Listener.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c = tls.Server(c, l.config)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewListener creates a Listener which accepts connections from an inner
|
||||
// Listener and wraps each connection with Server.
|
||||
// The configuration config must be non-nil and must have
|
||||
// at least one certificate.
|
||||
func newTLSKeepaliveListener(inner net.Listener, config *tls.Config) net.Listener {
|
||||
l := &tlsKeepaliveListener{}
|
||||
l.Listener = inner
|
||||
l.config = config
|
||||
return l
|
||||
}
|
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener_openbsd.go
generated
vendored
Normal file
26
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener_openbsd.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2023 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build openbsd
|
||||
|
||||
package transport
|
||||
|
||||
import "time"
|
||||
|
||||
// SetKeepAlivePeriod sets keepalive period
|
||||
func (l *keepAliveConn) SetKeepAlivePeriod(d time.Duration) error {
|
||||
// OpenBSD has no user-settable per-socket TCP keepalive options.
|
||||
// Refer to https://github.com/etcd-io/etcd/issues/15811.
|
||||
return nil
|
||||
}
|
24
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener_unix.go
generated
vendored
Normal file
24
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/keepalive_listener_unix.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2023 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !openbsd
|
||||
|
||||
package transport
|
||||
|
||||
import "time"
|
||||
|
||||
// SetKeepAlivePeriod sets keepalive period
|
||||
func (l *keepAliveConn) SetKeepAlivePeriod(d time.Duration) error {
|
||||
return l.TCPConn.SetKeepAlivePeriod(d)
|
||||
}
|
86
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/limit_listen.go
generated
vendored
Normal file
86
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/limit_listen.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2013 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 transport provides network utility functions, complementing the more
|
||||
// common ones in the net package.
|
||||
package transport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotTCP = errors.New("only tcp connections have keepalive")
|
||||
)
|
||||
|
||||
// LimitListener returns a Listener that accepts at most n simultaneous
|
||||
// connections from the provided Listener.
|
||||
func LimitListener(l net.Listener, n int) net.Listener {
|
||||
return &limitListener{l, make(chan struct{}, n)}
|
||||
}
|
||||
|
||||
type limitListener struct {
|
||||
net.Listener
|
||||
sem chan struct{}
|
||||
}
|
||||
|
||||
func (l *limitListener) acquire() { l.sem <- struct{}{} }
|
||||
func (l *limitListener) release() { <-l.sem }
|
||||
|
||||
func (l *limitListener) Accept() (net.Conn, error) {
|
||||
l.acquire()
|
||||
c, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
l.release()
|
||||
return nil, err
|
||||
}
|
||||
return &limitListenerConn{Conn: c, release: l.release}, nil
|
||||
}
|
||||
|
||||
type limitListenerConn struct {
|
||||
net.Conn
|
||||
releaseOnce sync.Once
|
||||
release func()
|
||||
}
|
||||
|
||||
func (l *limitListenerConn) Close() error {
|
||||
err := l.Conn.Close()
|
||||
l.releaseOnce.Do(l.release)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetKeepAlive sets keepalive
|
||||
//
|
||||
// Deprecated: use (*keepAliveConn) SetKeepAlive instead.
|
||||
func (l *limitListenerConn) SetKeepAlive(doKeepAlive bool) error {
|
||||
tcpc, ok := l.Conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return ErrNotTCP
|
||||
}
|
||||
return tcpc.SetKeepAlive(doKeepAlive)
|
||||
}
|
||||
|
||||
// SetKeepAlivePeriod sets keepalive period
|
||||
//
|
||||
// Deprecated: use (*keepAliveConn) SetKeepAlivePeriod instead.
|
||||
func (l *limitListenerConn) SetKeepAlivePeriod(d time.Duration) error {
|
||||
tcpc, ok := l.Conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return ErrNotTCP
|
||||
}
|
||||
return tcpc.SetKeepAlivePeriod(d)
|
||||
}
|
638
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener.go
generated
vendored
Normal file
638
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener.go
generated
vendored
Normal file
@ -0,0 +1,638 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.etcd.io/etcd/client/pkg/v3/fileutil"
|
||||
"go.etcd.io/etcd/client/pkg/v3/tlsutil"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// NewListener creates a new listner.
|
||||
func NewListener(addr, scheme string, tlsinfo *TLSInfo) (l net.Listener, err error) {
|
||||
return newListener(addr, scheme, WithTLSInfo(tlsinfo))
|
||||
}
|
||||
|
||||
// NewListenerWithOpts creates a new listener which accpets listener options.
|
||||
func NewListenerWithOpts(addr, scheme string, opts ...ListenerOption) (net.Listener, error) {
|
||||
return newListener(addr, scheme, opts...)
|
||||
}
|
||||
|
||||
func newListener(addr, scheme string, opts ...ListenerOption) (net.Listener, error) {
|
||||
if scheme == "unix" || scheme == "unixs" {
|
||||
// unix sockets via unix://laddr
|
||||
return NewUnixListener(addr)
|
||||
}
|
||||
|
||||
lnOpts := newListenOpts(opts...)
|
||||
|
||||
switch {
|
||||
case lnOpts.IsSocketOpts():
|
||||
// new ListenConfig with socket options.
|
||||
config, err := newListenConfig(lnOpts.socketOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lnOpts.ListenConfig = config
|
||||
// check for timeout
|
||||
fallthrough
|
||||
case lnOpts.IsTimeout(), lnOpts.IsSocketOpts():
|
||||
// timeout listener with socket options.
|
||||
ln, err := newKeepAliveListener(&lnOpts.ListenConfig, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lnOpts.Listener = &rwTimeoutListener{
|
||||
Listener: ln,
|
||||
readTimeout: lnOpts.readTimeout,
|
||||
writeTimeout: lnOpts.writeTimeout,
|
||||
}
|
||||
case lnOpts.IsTimeout():
|
||||
ln, err := newKeepAliveListener(nil, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lnOpts.Listener = &rwTimeoutListener{
|
||||
Listener: ln,
|
||||
readTimeout: lnOpts.readTimeout,
|
||||
writeTimeout: lnOpts.writeTimeout,
|
||||
}
|
||||
default:
|
||||
ln, err := newKeepAliveListener(nil, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lnOpts.Listener = ln
|
||||
}
|
||||
|
||||
// only skip if not passing TLSInfo
|
||||
if lnOpts.skipTLSInfoCheck && !lnOpts.IsTLS() {
|
||||
return lnOpts.Listener, nil
|
||||
}
|
||||
return wrapTLS(scheme, lnOpts.tlsInfo, lnOpts.Listener)
|
||||
}
|
||||
|
||||
func newKeepAliveListener(cfg *net.ListenConfig, addr string) (ln net.Listener, err error) {
|
||||
if cfg != nil {
|
||||
ln, err = cfg.Listen(context.TODO(), "tcp", addr)
|
||||
} else {
|
||||
ln, err = net.Listen("tcp", addr)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return NewKeepAliveListener(ln, "tcp", nil)
|
||||
}
|
||||
|
||||
func wrapTLS(scheme string, tlsinfo *TLSInfo, l net.Listener) (net.Listener, error) {
|
||||
if scheme != "https" && scheme != "unixs" {
|
||||
return l, nil
|
||||
}
|
||||
if tlsinfo != nil && tlsinfo.SkipClientSANVerify {
|
||||
return NewTLSListener(l, tlsinfo)
|
||||
}
|
||||
return newTLSListener(l, tlsinfo, checkSAN)
|
||||
}
|
||||
|
||||
func newListenConfig(sopts *SocketOpts) (net.ListenConfig, error) {
|
||||
lc := net.ListenConfig{}
|
||||
if sopts != nil {
|
||||
ctls := getControls(sopts)
|
||||
if len(ctls) > 0 {
|
||||
lc.Control = ctls.Control
|
||||
}
|
||||
}
|
||||
return lc, nil
|
||||
}
|
||||
|
||||
type TLSInfo struct {
|
||||
// CertFile is the _server_ cert, it will also be used as a _client_ certificate if ClientCertFile is empty
|
||||
CertFile string
|
||||
// KeyFile is the key for the CertFile
|
||||
KeyFile string
|
||||
// ClientCertFile is a _client_ cert for initiating connections when ClientCertAuth is defined. If ClientCertAuth
|
||||
// is true but this value is empty, the CertFile will be used instead.
|
||||
ClientCertFile string
|
||||
// ClientKeyFile is the key for the ClientCertFile
|
||||
ClientKeyFile string
|
||||
|
||||
TrustedCAFile string
|
||||
ClientCertAuth bool
|
||||
CRLFile string
|
||||
InsecureSkipVerify bool
|
||||
SkipClientSANVerify bool
|
||||
|
||||
// ServerName ensures the cert matches the given host in case of discovery / virtual hosting
|
||||
ServerName string
|
||||
|
||||
// HandshakeFailure is optionally called when a connection fails to handshake. The
|
||||
// connection will be closed immediately afterwards.
|
||||
HandshakeFailure func(*tls.Conn, error)
|
||||
|
||||
// CipherSuites is a list of supported cipher suites.
|
||||
// If empty, Go auto-populates it by default.
|
||||
// Note that cipher suites are prioritized in the given order.
|
||||
CipherSuites []uint16
|
||||
|
||||
// MinVersion is the minimum TLS version that is acceptable.
|
||||
// If not set, the minimum version is TLS 1.2.
|
||||
MinVersion uint16
|
||||
|
||||
// MaxVersion is the maximum TLS version that is acceptable.
|
||||
// If not set, the default used by Go is selected (see tls.Config.MaxVersion).
|
||||
MaxVersion uint16
|
||||
|
||||
selfCert bool
|
||||
|
||||
// parseFunc exists to simplify testing. Typically, parseFunc
|
||||
// should be left nil. In that case, tls.X509KeyPair will be used.
|
||||
parseFunc func([]byte, []byte) (tls.Certificate, error)
|
||||
|
||||
// AllowedCN is a CN which must be provided by a client.
|
||||
//
|
||||
// Deprecated: use AllowedCNs instead.
|
||||
AllowedCN string
|
||||
|
||||
// AllowedHostname is an IP address or hostname that must match the TLS
|
||||
// certificate provided by a client.
|
||||
//
|
||||
// Deprecated: use AllowedHostnames instead.
|
||||
AllowedHostname string
|
||||
|
||||
// AllowedCNs is a list of acceptable CNs which must be provided by a client.
|
||||
AllowedCNs []string
|
||||
|
||||
// AllowedHostnames is a list of acceptable IP addresses or hostnames that must match the
|
||||
// TLS certificate provided by a client.
|
||||
AllowedHostnames []string
|
||||
|
||||
// Logger logs TLS errors.
|
||||
// If nil, all logs are discarded.
|
||||
Logger *zap.Logger
|
||||
|
||||
// EmptyCN indicates that the cert must have empty CN.
|
||||
// If true, ClientConfig() will return an error for a cert with non empty CN.
|
||||
EmptyCN bool
|
||||
}
|
||||
|
||||
func (info TLSInfo) String() string {
|
||||
return fmt.Sprintf("cert = %s, key = %s, client-cert=%s, client-key=%s, trusted-ca = %s, client-cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.ClientCertFile, info.ClientKeyFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile)
|
||||
}
|
||||
|
||||
func (info TLSInfo) Empty() bool {
|
||||
return info.CertFile == "" && info.KeyFile == ""
|
||||
}
|
||||
|
||||
func SelfCert(lg *zap.Logger, dirpath string, hosts []string, selfSignedCertValidity uint, additionalUsages ...x509.ExtKeyUsage) (info TLSInfo, err error) {
|
||||
info.Logger = lg
|
||||
if selfSignedCertValidity == 0 {
|
||||
err = fmt.Errorf("selfSignedCertValidity is invalid,it should be greater than 0")
|
||||
info.Logger.Warn(
|
||||
"cannot generate cert",
|
||||
zap.Error(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
err = fileutil.TouchDirAll(lg, dirpath)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot create cert directory",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
certPath, err := filepath.Abs(filepath.Join(dirpath, "cert.pem"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
keyPath, err := filepath.Abs(filepath.Join(dirpath, "key.pem"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, errcert := os.Stat(certPath)
|
||||
_, errkey := os.Stat(keyPath)
|
||||
if errcert == nil && errkey == nil {
|
||||
info.CertFile = certPath
|
||||
info.KeyFile = keyPath
|
||||
info.ClientCertFile = certPath
|
||||
info.ClientKeyFile = keyPath
|
||||
info.selfCert = true
|
||||
return
|
||||
}
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot generate random number",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
tmpl := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{Organization: []string{"etcd"}},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().Add(time.Duration(selfSignedCertValidity) * 365 * (24 * time.Hour)),
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: append([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, additionalUsages...),
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"automatically generate certificates",
|
||||
zap.Time("certificate-validity-bound-not-after", tmpl.NotAfter),
|
||||
)
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
h, _, _ := net.SplitHostPort(host)
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
|
||||
} else {
|
||||
tmpl.DNSNames = append(tmpl.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot generate ECDSA key",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot generate x509 certificate",
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
certOut, err := os.Create(certPath)
|
||||
if err != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot cert file",
|
||||
zap.String("path", certPath),
|
||||
zap.Error(err),
|
||||
)
|
||||
return
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
if info.Logger != nil {
|
||||
info.Logger.Info("created cert file", zap.String("path", certPath))
|
||||
}
|
||||
|
||||
b, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"cannot key file",
|
||||
zap.String("path", keyPath),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
|
||||
keyOut.Close()
|
||||
if info.Logger != nil {
|
||||
info.Logger.Info("created key file", zap.String("path", keyPath))
|
||||
}
|
||||
return SelfCert(lg, dirpath, hosts, selfSignedCertValidity)
|
||||
}
|
||||
|
||||
// baseConfig is called on initial TLS handshake start.
|
||||
//
|
||||
// Previously,
|
||||
// 1. Server has non-empty (*tls.Config).Certificates on client hello
|
||||
// 2. Server calls (*tls.Config).GetCertificate iff:
|
||||
// - Server's (*tls.Config).Certificates is not empty, or
|
||||
// - Client supplies SNI; non-empty (*tls.ClientHelloInfo).ServerName
|
||||
//
|
||||
// When (*tls.Config).Certificates is always populated on initial handshake,
|
||||
// client is expected to provide a valid matching SNI to pass the TLS
|
||||
// verification, thus trigger server (*tls.Config).GetCertificate to reload
|
||||
// TLS assets. However, a cert whose SAN field does not include domain names
|
||||
// but only IP addresses, has empty (*tls.ClientHelloInfo).ServerName, thus
|
||||
// it was never able to trigger TLS reload on initial handshake; first
|
||||
// ceritifcate object was being used, never being updated.
|
||||
//
|
||||
// Now, (*tls.Config).Certificates is created empty on initial TLS client
|
||||
// handshake, in order to trigger (*tls.Config).GetCertificate and populate
|
||||
// rest of the certificates on every new TLS connection, even when client
|
||||
// SNI is empty (e.g. cert only includes IPs).
|
||||
func (info TLSInfo) baseConfig() (*tls.Config, error) {
|
||||
if info.KeyFile == "" || info.CertFile == "" {
|
||||
return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
|
||||
}
|
||||
if info.Logger == nil {
|
||||
info.Logger = zap.NewNop()
|
||||
}
|
||||
|
||||
_, err := tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Perform prevalidation of client cert and key if either are provided. This makes sure we crash before accepting any connections.
|
||||
if (info.ClientKeyFile == "") != (info.ClientCertFile == "") {
|
||||
return nil, fmt.Errorf("ClientKeyFile and ClientCertFile must both be present or both absent: key: %v, cert: %v]", info.ClientKeyFile, info.ClientCertFile)
|
||||
}
|
||||
if info.ClientCertFile != "" {
|
||||
_, err := tlsutil.NewCert(info.ClientCertFile, info.ClientKeyFile, info.parseFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var minVersion uint16
|
||||
if info.MinVersion != 0 {
|
||||
minVersion = info.MinVersion
|
||||
} else {
|
||||
// Default minimum version is TLS 1.2, previous versions are insecure and deprecated.
|
||||
minVersion = tls.VersionTLS12
|
||||
}
|
||||
|
||||
cfg := &tls.Config{
|
||||
MinVersion: minVersion,
|
||||
MaxVersion: info.MaxVersion,
|
||||
ServerName: info.ServerName,
|
||||
}
|
||||
|
||||
if len(info.CipherSuites) > 0 {
|
||||
cfg.CipherSuites = info.CipherSuites
|
||||
}
|
||||
|
||||
// Client certificates may be verified by either an exact match on the CN,
|
||||
// or a more general check of the CN and SANs.
|
||||
var verifyCertificate func(*x509.Certificate) bool
|
||||
|
||||
if info.AllowedCN != "" && len(info.AllowedCNs) > 0 {
|
||||
return nil, fmt.Errorf("AllowedCN and AllowedCNs are mutually exclusive (cn=%q, cns=%q)", info.AllowedCN, info.AllowedCNs)
|
||||
}
|
||||
if info.AllowedHostname != "" && len(info.AllowedHostnames) > 0 {
|
||||
return nil, fmt.Errorf("AllowedHostname and AllowedHostnames are mutually exclusive (hostname=%q, hostnames=%q)", info.AllowedHostname, info.AllowedHostnames)
|
||||
}
|
||||
if info.AllowedCN != "" && info.AllowedHostname != "" {
|
||||
return nil, fmt.Errorf("AllowedCN and AllowedHostname are mutually exclusive (cn=%q, hostname=%q)", info.AllowedCN, info.AllowedHostname)
|
||||
}
|
||||
if len(info.AllowedCNs) > 0 && len(info.AllowedHostnames) > 0 {
|
||||
return nil, fmt.Errorf("AllowedCNs and AllowedHostnames are mutually exclusive (cns=%q, hostnames=%q)", info.AllowedCNs, info.AllowedHostnames)
|
||||
}
|
||||
|
||||
if info.AllowedCN != "" {
|
||||
info.Logger.Warn("AllowedCN is deprecated, use AllowedCNs instead")
|
||||
verifyCertificate = func(cert *x509.Certificate) bool {
|
||||
return info.AllowedCN == cert.Subject.CommonName
|
||||
}
|
||||
}
|
||||
if info.AllowedHostname != "" {
|
||||
info.Logger.Warn("AllowedHostname is deprecated, use AllowedHostnames instead")
|
||||
verifyCertificate = func(cert *x509.Certificate) bool {
|
||||
return cert.VerifyHostname(info.AllowedHostname) == nil
|
||||
}
|
||||
}
|
||||
if len(info.AllowedCNs) > 0 {
|
||||
verifyCertificate = func(cert *x509.Certificate) bool {
|
||||
for _, allowedCN := range info.AllowedCNs {
|
||||
if allowedCN == cert.Subject.CommonName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(info.AllowedHostnames) > 0 {
|
||||
verifyCertificate = func(cert *x509.Certificate) bool {
|
||||
for _, allowedHostname := range info.AllowedHostnames {
|
||||
if cert.VerifyHostname(allowedHostname) == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
if verifyCertificate != nil {
|
||||
cfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
for _, chains := range verifiedChains {
|
||||
if len(chains) != 0 {
|
||||
if verifyCertificate(chains[0]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New("client certificate authentication failed")
|
||||
}
|
||||
}
|
||||
|
||||
// this only reloads certs when there's a client request
|
||||
// TODO: support server-side refresh (e.g. inotify, SIGHUP), caching
|
||||
cfg.GetCertificate = func(clientHello *tls.ClientHelloInfo) (cert *tls.Certificate, err error) {
|
||||
cert, err = tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
|
||||
if os.IsNotExist(err) {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"failed to find peer cert files",
|
||||
zap.String("cert-file", info.CertFile),
|
||||
zap.String("key-file", info.KeyFile),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
} else if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"failed to create peer certificate",
|
||||
zap.String("cert-file", info.CertFile),
|
||||
zap.String("key-file", info.KeyFile),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
return cert, err
|
||||
}
|
||||
cfg.GetClientCertificate = func(unused *tls.CertificateRequestInfo) (cert *tls.Certificate, err error) {
|
||||
certfile, keyfile := info.CertFile, info.KeyFile
|
||||
if info.ClientCertFile != "" {
|
||||
certfile, keyfile = info.ClientCertFile, info.ClientKeyFile
|
||||
}
|
||||
cert, err = tlsutil.NewCert(certfile, keyfile, info.parseFunc)
|
||||
if os.IsNotExist(err) {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"failed to find client cert files",
|
||||
zap.String("cert-file", certfile),
|
||||
zap.String("key-file", keyfile),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
} else if err != nil {
|
||||
if info.Logger != nil {
|
||||
info.Logger.Warn(
|
||||
"failed to create client certificate",
|
||||
zap.String("cert-file", certfile),
|
||||
zap.String("key-file", keyfile),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
}
|
||||
return cert, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// cafiles returns a list of CA file paths.
|
||||
func (info TLSInfo) cafiles() []string {
|
||||
cs := make([]string, 0)
|
||||
if info.TrustedCAFile != "" {
|
||||
cs = append(cs, info.TrustedCAFile)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// ServerConfig generates a tls.Config object for use by an HTTP server.
|
||||
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
||||
cfg, err := info.baseConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if info.Logger == nil {
|
||||
info.Logger = zap.NewNop()
|
||||
}
|
||||
|
||||
cfg.ClientAuth = tls.NoClientCert
|
||||
if info.TrustedCAFile != "" || info.ClientCertAuth {
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
cs := info.cafiles()
|
||||
if len(cs) > 0 {
|
||||
info.Logger.Info("Loading cert pool", zap.Strings("cs", cs),
|
||||
zap.Any("tlsinfo", info))
|
||||
cp, err := tlsutil.NewCertPool(cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.ClientCAs = cp
|
||||
}
|
||||
|
||||
// "h2" NextProtos is necessary for enabling HTTP2 for go's HTTP server
|
||||
cfg.NextProtos = []string{"h2"}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// ClientConfig generates a tls.Config object for use by an HTTP client.
|
||||
func (info TLSInfo) ClientConfig() (*tls.Config, error) {
|
||||
var cfg *tls.Config
|
||||
var err error
|
||||
|
||||
if !info.Empty() {
|
||||
cfg, err = info.baseConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
cfg = &tls.Config{ServerName: info.ServerName}
|
||||
}
|
||||
cfg.InsecureSkipVerify = info.InsecureSkipVerify
|
||||
|
||||
cs := info.cafiles()
|
||||
if len(cs) > 0 {
|
||||
cfg.RootCAs, err = tlsutil.NewCertPool(cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if info.selfCert {
|
||||
cfg.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
if info.EmptyCN {
|
||||
hasNonEmptyCN := false
|
||||
cn := ""
|
||||
_, err := tlsutil.NewCert(info.CertFile, info.KeyFile, func(certPEMBlock []byte, keyPEMBlock []byte) (tls.Certificate, error) {
|
||||
var block *pem.Block
|
||||
block, _ = pem.Decode(certPEMBlock)
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
if len(cert.Subject.CommonName) != 0 {
|
||||
hasNonEmptyCN = true
|
||||
cn = cert.Subject.CommonName
|
||||
}
|
||||
return tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasNonEmptyCN {
|
||||
return nil, fmt.Errorf("cert has non empty Common Name (%s): %s", cn, info.CertFile)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// IsClosedConnError returns true if the error is from closing listener, cmux.
|
||||
// copied from golang.org/x/net/http2/http2.go
|
||||
func IsClosedConnError(err error) bool {
|
||||
// 'use of closed network connection' (Go <=1.8)
|
||||
// 'use of closed file or network connection' (Go >1.8, internal/poll.ErrClosing)
|
||||
// 'mux: listener closed' (cmux.ErrListenerClosed)
|
||||
return err != nil && strings.Contains(err.Error(), "closed")
|
||||
}
|
76
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_opts.go
generated
vendored
Normal file
76
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_opts.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ListenerOptions struct {
|
||||
Listener net.Listener
|
||||
ListenConfig net.ListenConfig
|
||||
|
||||
socketOpts *SocketOpts
|
||||
tlsInfo *TLSInfo
|
||||
skipTLSInfoCheck bool
|
||||
writeTimeout time.Duration
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func newListenOpts(opts ...ListenerOption) *ListenerOptions {
|
||||
lnOpts := &ListenerOptions{}
|
||||
lnOpts.applyOpts(opts)
|
||||
return lnOpts
|
||||
}
|
||||
|
||||
func (lo *ListenerOptions) applyOpts(opts []ListenerOption) {
|
||||
for _, opt := range opts {
|
||||
opt(lo)
|
||||
}
|
||||
}
|
||||
|
||||
// IsTimeout returns true if the listener has a read/write timeout defined.
|
||||
func (lo *ListenerOptions) IsTimeout() bool { return lo.readTimeout != 0 || lo.writeTimeout != 0 }
|
||||
|
||||
// IsSocketOpts returns true if the listener options includes socket options.
|
||||
func (lo *ListenerOptions) IsSocketOpts() bool {
|
||||
if lo.socketOpts == nil {
|
||||
return false
|
||||
}
|
||||
return lo.socketOpts.ReusePort || lo.socketOpts.ReuseAddress
|
||||
}
|
||||
|
||||
// IsTLS returns true if listner options includes TLSInfo.
|
||||
func (lo *ListenerOptions) IsTLS() bool {
|
||||
if lo.tlsInfo == nil {
|
||||
return false
|
||||
}
|
||||
return !lo.tlsInfo.Empty()
|
||||
}
|
||||
|
||||
// ListenerOption are options which can be applied to the listener.
|
||||
type ListenerOption func(*ListenerOptions)
|
||||
|
||||
// WithTimeout allows for a read or write timeout to be applied to the listener.
|
||||
func WithTimeout(read, write time.Duration) ListenerOption {
|
||||
return func(lo *ListenerOptions) {
|
||||
lo.writeTimeout = write
|
||||
lo.readTimeout = read
|
||||
}
|
||||
}
|
||||
|
||||
// WithSocketOpts defines socket options that will be applied to the listener.
|
||||
func WithSocketOpts(s *SocketOpts) ListenerOption {
|
||||
return func(lo *ListenerOptions) { lo.socketOpts = s }
|
||||
}
|
||||
|
||||
// WithTLSInfo adds TLS credentials to the listener.
|
||||
func WithTLSInfo(t *TLSInfo) ListenerOption {
|
||||
return func(lo *ListenerOptions) { lo.tlsInfo = t }
|
||||
}
|
||||
|
||||
// WithSkipTLSInfoCheck when true a transport can be created with an https scheme
|
||||
// without passing TLSInfo, circumventing not presented error. Skipping this check
|
||||
// also requires that TLSInfo is not passed.
|
||||
func WithSkipTLSInfoCheck(skip bool) ListenerOption {
|
||||
return func(lo *ListenerOptions) { lo.skipTLSInfoCheck = skip }
|
||||
}
|
272
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_tls.go
generated
vendored
Normal file
272
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_tls.go
generated
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// tlsListener overrides a TLS listener so it will reject client
|
||||
// certificates with insufficient SAN credentials or CRL revoked
|
||||
// certificates.
|
||||
type tlsListener struct {
|
||||
net.Listener
|
||||
connc chan net.Conn
|
||||
donec chan struct{}
|
||||
err error
|
||||
handshakeFailure func(*tls.Conn, error)
|
||||
check tlsCheckFunc
|
||||
}
|
||||
|
||||
type tlsCheckFunc func(context.Context, *tls.Conn) error
|
||||
|
||||
// NewTLSListener handshakes TLS connections and performs optional CRL checking.
|
||||
func NewTLSListener(l net.Listener, tlsinfo *TLSInfo) (net.Listener, error) {
|
||||
check := func(context.Context, *tls.Conn) error { return nil }
|
||||
return newTLSListener(l, tlsinfo, check)
|
||||
}
|
||||
|
||||
func newTLSListener(l net.Listener, tlsinfo *TLSInfo, check tlsCheckFunc) (net.Listener, error) {
|
||||
if tlsinfo == nil || tlsinfo.Empty() {
|
||||
l.Close()
|
||||
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", l.Addr().String())
|
||||
}
|
||||
tlscfg, err := tlsinfo.ServerConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hf := tlsinfo.HandshakeFailure
|
||||
if hf == nil {
|
||||
hf = func(*tls.Conn, error) {}
|
||||
}
|
||||
|
||||
if len(tlsinfo.CRLFile) > 0 {
|
||||
prevCheck := check
|
||||
check = func(ctx context.Context, tlsConn *tls.Conn) error {
|
||||
if err := prevCheck(ctx, tlsConn); err != nil {
|
||||
return err
|
||||
}
|
||||
st := tlsConn.ConnectionState()
|
||||
if certs := st.PeerCertificates; len(certs) > 0 {
|
||||
return checkCRL(tlsinfo.CRLFile, certs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
tlsl := &tlsListener{
|
||||
Listener: tls.NewListener(l, tlscfg),
|
||||
connc: make(chan net.Conn),
|
||||
donec: make(chan struct{}),
|
||||
handshakeFailure: hf,
|
||||
check: check,
|
||||
}
|
||||
go tlsl.acceptLoop()
|
||||
return tlsl, nil
|
||||
}
|
||||
|
||||
func (l *tlsListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case conn := <-l.connc:
|
||||
return conn, nil
|
||||
case <-l.donec:
|
||||
return nil, l.err
|
||||
}
|
||||
}
|
||||
|
||||
func checkSAN(ctx context.Context, tlsConn *tls.Conn) error {
|
||||
st := tlsConn.ConnectionState()
|
||||
if certs := st.PeerCertificates; len(certs) > 0 {
|
||||
addr := tlsConn.RemoteAddr().String()
|
||||
return checkCertSAN(ctx, certs[0], addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// acceptLoop launches each TLS handshake in a separate goroutine
|
||||
// to prevent a hanging TLS connection from blocking other connections.
|
||||
func (l *tlsListener) acceptLoop() {
|
||||
var wg sync.WaitGroup
|
||||
var pendingMu sync.Mutex
|
||||
|
||||
pending := make(map[net.Conn]struct{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
cancel()
|
||||
pendingMu.Lock()
|
||||
for c := range pending {
|
||||
c.Close()
|
||||
}
|
||||
pendingMu.Unlock()
|
||||
wg.Wait()
|
||||
close(l.donec)
|
||||
}()
|
||||
|
||||
for {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
l.err = err
|
||||
return
|
||||
}
|
||||
|
||||
pendingMu.Lock()
|
||||
pending[conn] = struct{}{}
|
||||
pendingMu.Unlock()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
tlsConn := conn.(*tls.Conn)
|
||||
herr := tlsConn.Handshake()
|
||||
pendingMu.Lock()
|
||||
delete(pending, conn)
|
||||
pendingMu.Unlock()
|
||||
|
||||
if herr != nil {
|
||||
l.handshakeFailure(tlsConn, herr)
|
||||
return
|
||||
}
|
||||
if err := l.check(ctx, tlsConn); err != nil {
|
||||
l.handshakeFailure(tlsConn, err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case l.connc <- tlsConn:
|
||||
conn = nil
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func checkCRL(crlPath string, cert []*x509.Certificate) error {
|
||||
// TODO: cache
|
||||
crlBytes, err := ioutil.ReadFile(crlPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
certList, err := x509.ParseCRL(crlBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokedSerials := make(map[string]struct{})
|
||||
for _, rc := range certList.TBSCertList.RevokedCertificates {
|
||||
revokedSerials[string(rc.SerialNumber.Bytes())] = struct{}{}
|
||||
}
|
||||
for _, c := range cert {
|
||||
serial := string(c.SerialNumber.Bytes())
|
||||
if _, ok := revokedSerials[serial]; ok {
|
||||
return fmt.Errorf("transport: certificate serial %x revoked", serial)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCertSAN(ctx context.Context, cert *x509.Certificate, remoteAddr string) error {
|
||||
if len(cert.IPAddresses) == 0 && len(cert.DNSNames) == 0 {
|
||||
return nil
|
||||
}
|
||||
h, _, herr := net.SplitHostPort(remoteAddr)
|
||||
if herr != nil {
|
||||
return herr
|
||||
}
|
||||
if len(cert.IPAddresses) > 0 {
|
||||
cerr := cert.VerifyHostname(h)
|
||||
if cerr == nil {
|
||||
return nil
|
||||
}
|
||||
if len(cert.DNSNames) == 0 {
|
||||
return cerr
|
||||
}
|
||||
}
|
||||
if len(cert.DNSNames) > 0 {
|
||||
ok, err := isHostInDNS(ctx, h, cert.DNSNames)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = " (" + err.Error() + ")"
|
||||
}
|
||||
return fmt.Errorf("tls: %q does not match any of DNSNames %q"+errStr, h, cert.DNSNames)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isHostInDNS(ctx context.Context, host string, dnsNames []string) (ok bool, err error) {
|
||||
// reverse lookup
|
||||
wildcards, names := []string{}, []string{}
|
||||
for _, dns := range dnsNames {
|
||||
if strings.HasPrefix(dns, "*.") {
|
||||
wildcards = append(wildcards, dns[1:])
|
||||
} else {
|
||||
names = append(names, dns)
|
||||
}
|
||||
}
|
||||
lnames, lerr := net.DefaultResolver.LookupAddr(ctx, host)
|
||||
for _, name := range lnames {
|
||||
// strip trailing '.' from PTR record
|
||||
if name[len(name)-1] == '.' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
for _, wc := range wildcards {
|
||||
if strings.HasSuffix(name, wc) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, n := range names {
|
||||
if n == name {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
err = lerr
|
||||
|
||||
// forward lookup
|
||||
for _, dns := range names {
|
||||
addrs, lerr := net.DefaultResolver.LookupHost(ctx, dns)
|
||||
if lerr != nil {
|
||||
err = lerr
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if addr == host {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func (l *tlsListener) Close() error {
|
||||
err := l.Listener.Close()
|
||||
<-l.donec
|
||||
return err
|
||||
}
|
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt.go
generated
vendored
Normal file
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Controls []func(network, addr string, conn syscall.RawConn) error
|
||||
|
||||
func (ctls Controls) Control(network, addr string, conn syscall.RawConn) error {
|
||||
for _, s := range ctls {
|
||||
if err := s(network, addr, conn); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SocketOpts struct {
|
||||
// ReusePort enables socket option SO_REUSEPORT [1] which allows rebind of
|
||||
// a port already in use. User should keep in mind that flock can fail
|
||||
// in which case lock on data file could result in unexpected
|
||||
// condition. User should take caution to protect against lock race.
|
||||
// [1] https://man7.org/linux/man-pages/man7/socket.7.html
|
||||
ReusePort bool `json:"reuse-port"`
|
||||
// ReuseAddress enables a socket option SO_REUSEADDR which allows
|
||||
// binding to an address in `TIME_WAIT` state. Useful to improve MTTR
|
||||
// in cases where etcd slow to restart due to excessive `TIME_WAIT`.
|
||||
// [1] https://man7.org/linux/man-pages/man7/socket.7.html
|
||||
ReuseAddress bool `json:"reuse-address"`
|
||||
}
|
||||
|
||||
func getControls(sopts *SocketOpts) Controls {
|
||||
ctls := Controls{}
|
||||
if sopts.ReuseAddress {
|
||||
ctls = append(ctls, setReuseAddress)
|
||||
}
|
||||
if sopts.ReusePort {
|
||||
ctls = append(ctls, setReusePort)
|
||||
}
|
||||
return ctls
|
||||
}
|
||||
|
||||
func (sopts *SocketOpts) Empty() bool {
|
||||
return !sopts.ReuseAddress && !sopts.ReusePort
|
||||
}
|
35
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_solaris.go
generated
vendored
Normal file
35
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_solaris.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2021 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build solaris
|
||||
// +build solaris
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setReusePort(network, address string, c syscall.RawConn) error {
|
||||
return fmt.Errorf("port reuse is not supported on Solaris")
|
||||
}
|
||||
|
||||
func setReuseAddress(network, address string, conn syscall.RawConn) error {
|
||||
return conn.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1)
|
||||
})
|
||||
}
|
36
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_unix.go
generated
vendored
Normal file
36
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_unix.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2021 The etcd Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows && !solaris
|
||||
// +build !windows,!solaris
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setReusePort(network, address string, conn syscall.RawConn) error {
|
||||
return conn.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func setReuseAddress(network, address string, conn syscall.RawConn) error {
|
||||
return conn.Control(func(fd uintptr) {
|
||||
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1)
|
||||
})
|
||||
}
|
19
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_windows.go
generated
vendored
Normal file
19
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/sockopt_windows.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setReusePort(network, address string, c syscall.RawConn) error {
|
||||
return fmt.Errorf("port reuse is not supported on Windows")
|
||||
}
|
||||
|
||||
// Windows supports SO_REUSEADDR, but it may cause undefined behavior, as
|
||||
// there is no protection against port hijacking.
|
||||
func setReuseAddress(network, addr string, conn syscall.RawConn) error {
|
||||
return fmt.Errorf("address reuse is not supported on Windows")
|
||||
}
|
44
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_conn.go
generated
vendored
Normal file
44
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_conn.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type timeoutConn struct {
|
||||
net.Conn
|
||||
writeTimeout time.Duration
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func (c timeoutConn) Write(b []byte) (n int, err error) {
|
||||
if c.writeTimeout > 0 {
|
||||
if err := c.SetWriteDeadline(time.Now().Add(c.writeTimeout)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (c timeoutConn) Read(b []byte) (n int, err error) {
|
||||
if c.readTimeout > 0 {
|
||||
if err := c.SetReadDeadline(time.Now().Add(c.readTimeout)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return c.Conn.Read(b)
|
||||
}
|
36
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_dialer.go
generated
vendored
Normal file
36
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_dialer.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type rwTimeoutDialer struct {
|
||||
wtimeoutd time.Duration
|
||||
rdtimeoutd time.Duration
|
||||
net.Dialer
|
||||
}
|
||||
|
||||
func (d *rwTimeoutDialer) Dial(network, address string) (net.Conn, error) {
|
||||
conn, err := d.Dialer.Dial(network, address)
|
||||
tconn := &timeoutConn{
|
||||
readTimeout: d.rdtimeoutd,
|
||||
writeTimeout: d.wtimeoutd,
|
||||
Conn: conn,
|
||||
}
|
||||
return tconn, err
|
||||
}
|
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_listener.go
generated
vendored
Normal file
45
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_listener.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewTimeoutListener returns a listener that listens on the given address.
|
||||
// If read/write on the accepted connection blocks longer than its time limit,
|
||||
// it will return timeout error.
|
||||
func NewTimeoutListener(addr string, scheme string, tlsinfo *TLSInfo, readTimeout, writeTimeout time.Duration) (net.Listener, error) {
|
||||
return newListener(addr, scheme, WithTimeout(readTimeout, writeTimeout), WithTLSInfo(tlsinfo))
|
||||
}
|
||||
|
||||
type rwTimeoutListener struct {
|
||||
net.Listener
|
||||
writeTimeout time.Duration
|
||||
readTimeout time.Duration
|
||||
}
|
||||
|
||||
func (rwln *rwTimeoutListener) Accept() (net.Conn, error) {
|
||||
c, err := rwln.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return timeoutConn{
|
||||
Conn: c,
|
||||
writeTimeout: rwln.writeTimeout,
|
||||
readTimeout: rwln.readTimeout,
|
||||
}, nil
|
||||
}
|
51
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_transport.go
generated
vendored
Normal file
51
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_transport.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NewTimeoutTransport returns a transport created using the given TLS info.
|
||||
// If read/write on the created connection blocks longer than its time limit,
|
||||
// it will return timeout error.
|
||||
// If read/write timeout is set, transport will not be able to reuse connection.
|
||||
func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.Duration) (*http.Transport, error) {
|
||||
tr, err := NewTransport(info, dialtimeoutd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rdtimeoutd != 0 || wtimeoutd != 0 {
|
||||
// the timed out connection will timeout soon after it is idle.
|
||||
// it should not be put back to http transport as an idle connection for future usage.
|
||||
tr.MaxIdleConnsPerHost = -1
|
||||
} else {
|
||||
// allow more idle connections between peers to avoid unnecessary port allocation.
|
||||
tr.MaxIdleConnsPerHost = 1024
|
||||
}
|
||||
|
||||
tr.Dial = (&rwTimeoutDialer{
|
||||
Dialer: net.Dialer{
|
||||
Timeout: dialtimeoutd,
|
||||
KeepAlive: 30 * time.Second,
|
||||
},
|
||||
rdtimeoutd: rdtimeoutd,
|
||||
wtimeoutd: wtimeoutd,
|
||||
}).Dial
|
||||
return tr, nil
|
||||
}
|
52
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/tls.go
generated
vendored
Normal file
52
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/tls.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ValidateSecureEndpoints scans the given endpoints against tls info, returning only those
|
||||
// endpoints that could be validated as secure.
|
||||
func ValidateSecureEndpoints(tlsInfo TLSInfo, eps []string) ([]string, error) {
|
||||
t, err := NewTransport(tlsInfo, 5*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer t.CloseIdleConnections()
|
||||
|
||||
var errs []string
|
||||
var endpoints []string
|
||||
for _, ep := range eps {
|
||||
if !strings.HasPrefix(ep, "https://") {
|
||||
errs = append(errs, fmt.Sprintf("%q is insecure", ep))
|
||||
continue
|
||||
}
|
||||
conn, cerr := t.DialContext(context.Background(), "tcp", ep[len("https://"):])
|
||||
if cerr != nil {
|
||||
errs = append(errs, fmt.Sprintf("%q failed to dial (%v)", ep, cerr))
|
||||
continue
|
||||
}
|
||||
conn.Close()
|
||||
endpoints = append(endpoints, ep)
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
err = fmt.Errorf("%s", strings.Join(errs, ","))
|
||||
}
|
||||
return endpoints, err
|
||||
}
|
77
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/transport.go
generated
vendored
Normal file
77
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/transport.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type unixTransport struct{ *http.Transport }
|
||||
|
||||
func NewTransport(info TLSInfo, dialtimeoutd time.Duration) (*http.Transport, error) {
|
||||
cfg, err := info.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: dialtimeoutd,
|
||||
// value taken from http.DefaultTransport
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
// value taken from http.DefaultTransport
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: cfg,
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: dialtimeoutd,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
dialContext := func(ctx context.Context, net, addr string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, "unix", addr)
|
||||
}
|
||||
tu := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: dialContext,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: cfg,
|
||||
// Cost of reopening connection on sockets is low, and they are mostly used in testing.
|
||||
// Long living unix-transport connections were leading to 'leak' test flakes.
|
||||
// Alternativly the returned Transport (t) should override CloseIdleConnections to
|
||||
// forward it to 'tu' as well.
|
||||
IdleConnTimeout: time.Microsecond,
|
||||
}
|
||||
ut := &unixTransport{tu}
|
||||
|
||||
t.RegisterProtocol("unix", ut)
|
||||
t.RegisterProtocol("unixs", ut)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (urt *unixTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
url := *req.URL
|
||||
req.URL = &url
|
||||
req.URL.Scheme = strings.Replace(req.URL.Scheme, "unix", "http", 1)
|
||||
return urt.Transport.RoundTrip(req)
|
||||
}
|
40
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/unix_listener.go
generated
vendored
Normal file
40
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/transport/unix_listener.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 transport
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
type unixListener struct{ net.Listener }
|
||||
|
||||
func NewUnixListener(addr string) (net.Listener, error) {
|
||||
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
l, err := net.Listen("unix", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &unixListener{l}, nil
|
||||
}
|
||||
|
||||
func (ul *unixListener) Close() error {
|
||||
if err := os.Remove(ul.Addr().String()); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return ul.Listener.Close()
|
||||
}
|
17
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/doc.go
generated
vendored
Normal file
17
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/doc.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 types declares various data types and implements type-checking
|
||||
// functions.
|
||||
package types
|
39
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/id.go
generated
vendored
Normal file
39
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/id.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// 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 types
|
||||
|
||||
import "strconv"
|
||||
|
||||
// ID represents a generic identifier which is canonically
|
||||
// stored as a uint64 but is typically represented as a
|
||||
// base-16 string for input/output
|
||||
type ID uint64
|
||||
|
||||
func (i ID) String() string {
|
||||
return strconv.FormatUint(uint64(i), 16)
|
||||
}
|
||||
|
||||
// IDFromString attempts to create an ID from a base-16 string.
|
||||
func IDFromString(s string) (ID, error) {
|
||||
i, err := strconv.ParseUint(s, 16, 64)
|
||||
return ID(i), err
|
||||
}
|
||||
|
||||
// IDSlice implements the sort interface
|
||||
type IDSlice []ID
|
||||
|
||||
func (p IDSlice) Len() int { return len(p) }
|
||||
func (p IDSlice) Less(i, j int) bool { return uint64(p[i]) < uint64(p[j]) }
|
||||
func (p IDSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
195
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/set.go
generated
vendored
Normal file
195
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/set.go
generated
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Set interface {
|
||||
Add(string)
|
||||
Remove(string)
|
||||
Contains(string) bool
|
||||
Equals(Set) bool
|
||||
Length() int
|
||||
Values() []string
|
||||
Copy() Set
|
||||
Sub(Set) Set
|
||||
}
|
||||
|
||||
func NewUnsafeSet(values ...string) *unsafeSet {
|
||||
set := &unsafeSet{make(map[string]struct{})}
|
||||
for _, v := range values {
|
||||
set.Add(v)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func NewThreadsafeSet(values ...string) *tsafeSet {
|
||||
us := NewUnsafeSet(values...)
|
||||
return &tsafeSet{us, sync.RWMutex{}}
|
||||
}
|
||||
|
||||
type unsafeSet struct {
|
||||
d map[string]struct{}
|
||||
}
|
||||
|
||||
// Add adds a new value to the set (no-op if the value is already present)
|
||||
func (us *unsafeSet) Add(value string) {
|
||||
us.d[value] = struct{}{}
|
||||
}
|
||||
|
||||
// Remove removes the given value from the set
|
||||
func (us *unsafeSet) Remove(value string) {
|
||||
delete(us.d, value)
|
||||
}
|
||||
|
||||
// Contains returns whether the set contains the given value
|
||||
func (us *unsafeSet) Contains(value string) (exists bool) {
|
||||
_, exists = us.d[value]
|
||||
return exists
|
||||
}
|
||||
|
||||
// ContainsAll returns whether the set contains all given values
|
||||
func (us *unsafeSet) ContainsAll(values []string) bool {
|
||||
for _, s := range values {
|
||||
if !us.Contains(s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns whether the contents of two sets are identical
|
||||
func (us *unsafeSet) Equals(other Set) bool {
|
||||
v1 := sort.StringSlice(us.Values())
|
||||
v2 := sort.StringSlice(other.Values())
|
||||
v1.Sort()
|
||||
v2.Sort()
|
||||
return reflect.DeepEqual(v1, v2)
|
||||
}
|
||||
|
||||
// Length returns the number of elements in the set
|
||||
func (us *unsafeSet) Length() int {
|
||||
return len(us.d)
|
||||
}
|
||||
|
||||
// Values returns the values of the Set in an unspecified order.
|
||||
func (us *unsafeSet) Values() (values []string) {
|
||||
values = make([]string, 0)
|
||||
for val := range us.d {
|
||||
values = append(values, val)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// Copy creates a new Set containing the values of the first
|
||||
func (us *unsafeSet) Copy() Set {
|
||||
cp := NewUnsafeSet()
|
||||
for val := range us.d {
|
||||
cp.Add(val)
|
||||
}
|
||||
|
||||
return cp
|
||||
}
|
||||
|
||||
// Sub removes all elements in other from the set
|
||||
func (us *unsafeSet) Sub(other Set) Set {
|
||||
oValues := other.Values()
|
||||
result := us.Copy().(*unsafeSet)
|
||||
|
||||
for _, val := range oValues {
|
||||
if _, ok := result.d[val]; !ok {
|
||||
continue
|
||||
}
|
||||
delete(result.d, val)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
type tsafeSet struct {
|
||||
us *unsafeSet
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Add(value string) {
|
||||
ts.m.Lock()
|
||||
defer ts.m.Unlock()
|
||||
ts.us.Add(value)
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Remove(value string) {
|
||||
ts.m.Lock()
|
||||
defer ts.m.Unlock()
|
||||
ts.us.Remove(value)
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Contains(value string) (exists bool) {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
return ts.us.Contains(value)
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Equals(other Set) bool {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
|
||||
// If ts and other represent the same variable, avoid calling
|
||||
// ts.us.Equals(other), to avoid double RLock bug
|
||||
if _other, ok := other.(*tsafeSet); ok {
|
||||
if _other == ts {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return ts.us.Equals(other)
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Length() int {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
return ts.us.Length()
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Values() (values []string) {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
return ts.us.Values()
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Copy() Set {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
usResult := ts.us.Copy().(*unsafeSet)
|
||||
return &tsafeSet{usResult, sync.RWMutex{}}
|
||||
}
|
||||
|
||||
func (ts *tsafeSet) Sub(other Set) Set {
|
||||
ts.m.RLock()
|
||||
defer ts.m.RUnlock()
|
||||
|
||||
// If ts and other represent the same variable, avoid calling
|
||||
// ts.us.Sub(other), to avoid double RLock bug
|
||||
if _other, ok := other.(*tsafeSet); ok {
|
||||
if _other == ts {
|
||||
usResult := NewUnsafeSet()
|
||||
return &tsafeSet{usResult, sync.RWMutex{}}
|
||||
}
|
||||
}
|
||||
usResult := ts.us.Sub(other).(*unsafeSet)
|
||||
return &tsafeSet{usResult, sync.RWMutex{}}
|
||||
}
|
22
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/slice.go
generated
vendored
Normal file
22
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/slice.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 types
|
||||
|
||||
// Uint64Slice implements sort interface
|
||||
type Uint64Slice []uint64
|
||||
|
||||
func (p Uint64Slice) Len() int { return len(p) }
|
||||
func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
|
||||
func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
87
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/urls.go
generated
vendored
Normal file
87
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/urls.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type URLs []url.URL
|
||||
|
||||
func NewURLs(strs []string) (URLs, error) {
|
||||
all := make([]url.URL, len(strs))
|
||||
if len(all) == 0 {
|
||||
return nil, errors.New("no valid URLs given")
|
||||
}
|
||||
for i, in := range strs {
|
||||
in = strings.TrimSpace(in)
|
||||
u, err := url.Parse(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "http", "https":
|
||||
if _, _, err := net.SplitHostPort(u.Host); err != nil {
|
||||
return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
return nil, fmt.Errorf("URL must not contain a path: %s", in)
|
||||
}
|
||||
case "unix", "unixs":
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
|
||||
}
|
||||
all[i] = *u
|
||||
}
|
||||
us := URLs(all)
|
||||
us.Sort()
|
||||
return us, nil
|
||||
}
|
||||
|
||||
func MustNewURLs(strs []string) URLs {
|
||||
urls, err := NewURLs(strs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return urls
|
||||
}
|
||||
|
||||
func (us URLs) String() string {
|
||||
return strings.Join(us.StringSlice(), ",")
|
||||
}
|
||||
|
||||
func (us *URLs) Sort() {
|
||||
sort.Sort(us)
|
||||
}
|
||||
func (us URLs) Len() int { return len(us) }
|
||||
func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() }
|
||||
func (us URLs) Swap(i, j int) { us[i], us[j] = us[j], us[i] }
|
||||
|
||||
func (us URLs) StringSlice() []string {
|
||||
out := make([]string, len(us))
|
||||
for i := range us {
|
||||
out[i] = us[i].String()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
107
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/urlsmap.go
generated
vendored
Normal file
107
e2e/vendor/go.etcd.io/etcd/client/pkg/v3/types/urlsmap.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URLsMap is a map from a name to its URLs.
|
||||
type URLsMap map[string]URLs
|
||||
|
||||
// NewURLsMap returns a URLsMap instantiated from the given string,
|
||||
// which consists of discovery-formatted names-to-URLs, like:
|
||||
// mach0=http://1.1.1.1:2380,mach0=http://2.2.2.2::2380,mach1=http://3.3.3.3:2380,mach2=http://4.4.4.4:2380
|
||||
func NewURLsMap(s string) (URLsMap, error) {
|
||||
m := parse(s)
|
||||
|
||||
cl := URLsMap{}
|
||||
for name, urls := range m {
|
||||
us, err := NewURLs(urls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cl[name] = us
|
||||
}
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
// NewURLsMapFromStringMap takes a map of strings and returns a URLsMap. The
|
||||
// string values in the map can be multiple values separated by the sep string.
|
||||
func NewURLsMapFromStringMap(m map[string]string, sep string) (URLsMap, error) {
|
||||
var err error
|
||||
um := URLsMap{}
|
||||
for k, v := range m {
|
||||
um[k], err = NewURLs(strings.Split(v, sep))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return um, nil
|
||||
}
|
||||
|
||||
// String turns URLsMap into discovery-formatted name-to-URLs sorted by name.
|
||||
func (c URLsMap) String() string {
|
||||
var pairs []string
|
||||
for name, urls := range c {
|
||||
for _, url := range urls {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%s", name, url.String()))
|
||||
}
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return strings.Join(pairs, ",")
|
||||
}
|
||||
|
||||
// URLs returns a list of all URLs.
|
||||
// The returned list is sorted in ascending lexicographical order.
|
||||
func (c URLsMap) URLs() []string {
|
||||
var urls []string
|
||||
for _, us := range c {
|
||||
for _, u := range us {
|
||||
urls = append(urls, u.String())
|
||||
}
|
||||
}
|
||||
sort.Strings(urls)
|
||||
return urls
|
||||
}
|
||||
|
||||
// Len returns the size of URLsMap.
|
||||
func (c URLsMap) Len() int {
|
||||
return len(c)
|
||||
}
|
||||
|
||||
// parse parses the given string and returns a map listing the values specified for each key.
|
||||
func parse(s string) map[string][]string {
|
||||
m := make(map[string][]string)
|
||||
for s != "" {
|
||||
key := s
|
||||
if i := strings.IndexAny(key, ","); i >= 0 {
|
||||
key, s = key[:i], key[i+1:]
|
||||
} else {
|
||||
s = ""
|
||||
}
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
value := ""
|
||||
if i := strings.Index(key, "="); i >= 0 {
|
||||
key, value = key[:i], key[i+1:]
|
||||
}
|
||||
m[key] = append(m[key], value)
|
||||
}
|
||||
return m
|
||||
}
|
202
e2e/vendor/go.etcd.io/etcd/client/v3/LICENSE
generated
vendored
Normal file
202
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/README.md
generated
vendored
Normal file
92
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/auth.go
generated
vendored
Normal file
236
e2e/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), ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
|
||||
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{}, auth.callOpts...)
|
||||
return (*AuthEnableResponse)(resp), ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthDisable(ctx context.Context) (*AuthDisableResponse, error) {
|
||||
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{}, auth.callOpts...)
|
||||
return (*AuthDisableResponse)(resp), ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) AuthStatus(ctx context.Context) (*AuthStatusResponse, error) {
|
||||
resp, err := auth.remote.AuthStatus(ctx, &pb.AuthStatusRequest{}, auth.callOpts...)
|
||||
return (*AuthStatusResponse)(resp), ContextError(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), ContextError(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), ContextError(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), ContextError(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), ContextError(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), ContextError(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), ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) UserList(ctx context.Context) (*AuthUserListResponse, error) {
|
||||
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{}, auth.callOpts...)
|
||||
return (*AuthUserListResponse)(resp), ContextError(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), ContextError(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), ContextError(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), ContextError(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), ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (auth *authClient) RoleList(ctx context.Context) (*AuthRoleListResponse, error) {
|
||||
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{}, auth.callOpts...)
|
||||
return (*AuthRoleListResponse)(resp), ContextError(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), ContextError(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), ContextError(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)
|
||||
}
|
629
e2e/vendor/go.etcd.io/etcd/client/v3/client.go
generated
vendored
Normal file
629
e2e/vendor/go.etcd.io/etcd/client/v3/client.go
generated
vendored
Normal file
@ -0,0 +1,629 @@
|
||||
// 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), mu: 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 ContextError(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())
|
||||
}
|
||||
|
||||
unaryMaxRetries := defaultUnaryMaxRetries
|
||||
if c.cfg.MaxUnaryRetries > 0 {
|
||||
unaryMaxRetries = c.cfg.MaxUnaryRetries
|
||||
}
|
||||
|
||||
backoffWaitBetween := defaultBackoffWaitBetween
|
||||
if c.cfg.BackoffWaitBetween > 0 {
|
||||
backoffWaitBetween = c.cfg.BackoffWaitBetween
|
||||
}
|
||||
|
||||
backoffJitterFraction := defaultBackoffJitterFraction
|
||||
if c.cfg.BackoffJitterFraction > 0 {
|
||||
backoffJitterFraction = c.cfg.BackoffJitterFraction
|
||||
}
|
||||
|
||||
// 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(backoffWaitBetween, backoffJitterFraction))
|
||||
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(unaryMaxRetries), 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 {
|
||||
c.authTokenBundle.UpdateAuthToken("")
|
||||
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 < 4) {
|
||||
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
|
||||
}
|
||||
|
||||
// ContextError converts the error into an EtcdError if the error message matches one of
|
||||
// the defined messages; otherwise, it tries to retrieve the context error.
|
||||
func ContextError(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
e2e/vendor/go.etcd.io/etcd/client/v3/cluster.go
generated
vendored
Normal file
141
e2e/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, ContextError(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, ContextError(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, ContextError(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, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
return (*MemberPromoteResponse)(resp), nil
|
||||
}
|
51
e2e/vendor/go.etcd.io/etcd/client/v3/compact_op.go
generated
vendored
Normal file
51
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/compare.go
generated
vendored
Normal file
140
e2e/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)
|
||||
}
|
101
e2e/vendor/go.etcd.io/etcd/client/v3/config.go
generated
vendored
Normal file
101
e2e/vendor/go.etcd.io/etcd/client/v3/config.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// 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"`
|
||||
|
||||
// MaxUnaryRetries is the maximum number of retries for unary RPCs.
|
||||
MaxUnaryRetries uint `json:"max-unary-retries"`
|
||||
|
||||
// BackoffWaitBetween is the wait time before retrying an RPC.
|
||||
BackoffWaitBetween time.Duration `json:"backoff-wait-between"`
|
||||
|
||||
// BackoffJitterFraction is the jitter fraction to randomize backoff wait time.
|
||||
BackoffJitterFraction float64 `json:"backoff-jitter-fraction"`
|
||||
|
||||
// TODO: support custom balancer picker
|
||||
}
|
131
e2e/vendor/go.etcd.io/etcd/client/v3/credentials/credentials.go
generated
vendored
Normal file
131
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/ctx.go
generated
vendored
Normal file
50
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/doc.go
generated
vendored
Normal file
106
e2e/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
|
134
e2e/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go
generated
vendored
Normal file
134
e2e/vendor/go.etcd.io/etcd/client/v3/internal/endpoint/endpoint.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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, path.Base(absolutePath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
if strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "unixs://") {
|
||||
// legacy etcd local path
|
||||
schema, localPath := mustSplit2(ep, "://")
|
||||
return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
schema, localPath := mustSplit2(ep, ":")
|
||||
return "unix:" + localPath, path.Base(localPath), schemeToCredsRequirement(schema)
|
||||
}
|
||||
|
||||
if strings.Contains(ep, "://") {
|
||||
url, err := url.Parse(ep)
|
||||
if err != nil {
|
||||
return ep, ep, CREDS_OPTIONAL
|
||||
}
|
||||
if url.Scheme == "http" || url.Scheme == "https" {
|
||||
return url.Host, url.Host, schemeToCredsRequirement(url.Scheme)
|
||||
}
|
||||
return ep, url.Host, schemeToCredsRequirement(url.Scheme)
|
||||
}
|
||||
// Handles plain addresses like 10.0.0.44:437.
|
||||
return ep, 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
e2e/vendor/go.etcd.io/etcd/client/v3/internal/resolver/resolver.go
generated
vendored
Normal file
74
e2e/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)
|
||||
}
|
||||
}
|
136
e2e/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
generated
vendored
Normal file
136
e2e/vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2024 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 kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
// New creates Client from config.
|
||||
// Caller is responsible to call Close() to clean up client.
|
||||
func New(cfg clientv3.Config) (*Client, error) {
|
||||
c, err := clientv3.New(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kc := &Client{
|
||||
Client: c,
|
||||
}
|
||||
kc.Kubernetes = kc
|
||||
return kc, nil
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
*clientv3.Client
|
||||
Kubernetes Interface
|
||||
}
|
||||
|
||||
var _ Interface = (*Client)(nil)
|
||||
|
||||
func (k Client) Get(ctx context.Context, key string, opts GetOptions) (resp GetResponse, err error) {
|
||||
rangeResp, err := k.KV.Get(ctx, key, clientv3.WithRev(opts.Revision), clientv3.WithLimit(1))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Revision = rangeResp.Header.Revision
|
||||
if len(rangeResp.Kvs) == 1 {
|
||||
resp.KV = rangeResp.Kvs[0]
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (k Client) List(ctx context.Context, prefix string, opts ListOptions) (resp ListResponse, err error) {
|
||||
rangeStart := prefix
|
||||
if opts.Continue != "" {
|
||||
rangeStart = opts.Continue
|
||||
}
|
||||
rangeEnd := clientv3.GetPrefixRangeEnd(prefix)
|
||||
rangeResp, err := k.KV.Get(ctx, rangeStart, clientv3.WithRange(rangeEnd), clientv3.WithLimit(opts.Limit), clientv3.WithRev(opts.Revision))
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Kvs = rangeResp.Kvs
|
||||
resp.Count = rangeResp.Count
|
||||
resp.Revision = rangeResp.Header.Revision
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (k Client) Count(ctx context.Context, prefix string, _ CountOptions) (int64, error) {
|
||||
resp, err := k.KV.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithCountOnly())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.Count, nil
|
||||
}
|
||||
|
||||
func (k Client) OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts PutOptions) (resp PutResponse, err error) {
|
||||
txn := k.KV.Txn(ctx).If(
|
||||
clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
|
||||
).Then(
|
||||
clientv3.OpPut(key, string(value), clientv3.WithLease(opts.LeaseID)),
|
||||
)
|
||||
|
||||
if opts.GetOnFailure {
|
||||
txn = txn.Else(clientv3.OpGet(key))
|
||||
}
|
||||
|
||||
txnResp, err := txn.Commit()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Succeeded = txnResp.Succeeded
|
||||
resp.Revision = txnResp.Header.Revision
|
||||
if opts.GetOnFailure && !txnResp.Succeeded {
|
||||
if len(txnResp.Responses) == 0 {
|
||||
return resp, fmt.Errorf("invalid OptimisticPut response: %v", txnResp.Responses)
|
||||
}
|
||||
resp.KV = kvFromTxnResponse(txnResp.Responses[0])
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (k Client) OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts DeleteOptions) (resp DeleteResponse, err error) {
|
||||
txn := k.KV.Txn(ctx).If(
|
||||
clientv3.Compare(clientv3.ModRevision(key), "=", expectedRevision),
|
||||
).Then(
|
||||
clientv3.OpDelete(key),
|
||||
)
|
||||
if opts.GetOnFailure {
|
||||
txn = txn.Else(clientv3.OpGet(key))
|
||||
}
|
||||
txnResp, err := txn.Commit()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Succeeded = txnResp.Succeeded
|
||||
resp.Revision = txnResp.Header.Revision
|
||||
if opts.GetOnFailure && !txnResp.Succeeded {
|
||||
resp.KV = kvFromTxnResponse(txnResp.Responses[0])
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func kvFromTxnResponse(resp *pb.ResponseOp) *mvccpb.KeyValue {
|
||||
getResponse := resp.GetResponseRange()
|
||||
if len(getResponse.Kvs) == 1 {
|
||||
return getResponse.Kvs[0]
|
||||
}
|
||||
return nil
|
||||
}
|
140
e2e/vendor/go.etcd.io/etcd/client/v3/kubernetes/interface.go
generated
vendored
Normal file
140
e2e/vendor/go.etcd.io/etcd/client/v3/kubernetes/interface.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2024 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 kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
// Interface defines the minimal client-side interface that Kubernetes requires
|
||||
// to interact with etcd. Methods below are standard etcd operations with
|
||||
// semantics adjusted to better suit Kubernetes' needs.
|
||||
type Interface interface {
|
||||
// Get retrieves a single key-value pair from etcd.
|
||||
//
|
||||
// If opts.Revision is set to a non-zero value, the key-value pair is retrieved at the specified revision.
|
||||
// If the required revision has been compacted, the request will fail with ErrCompacted.
|
||||
Get(ctx context.Context, key string, opts GetOptions) (GetResponse, error)
|
||||
|
||||
// List retrieves key-value pairs with the specified prefix, ordered lexicographically by key.
|
||||
//
|
||||
// If opts.Revision is non-zero, the key-value pairs are retrieved at the specified revision.
|
||||
// If the required revision has been compacted, the request will fail with ErrCompacted.
|
||||
// If opts.Limit is greater than zero, the number of returned key-value pairs is bounded by the limit.
|
||||
// If opts.Continue is not empty, the listing will start from the key immediately after the one specified by Continue.
|
||||
// The Continue value should be the last key returned in a previous paginated ListResponse.
|
||||
List(ctx context.Context, prefix string, opts ListOptions) (ListResponse, error)
|
||||
|
||||
// Count returns the number of keys with the specified prefix.
|
||||
//
|
||||
// Currently, there are no options for the Count operation. However, a placeholder options struct (CountOptions)
|
||||
// is provided for future extensibility in case options become necessary.
|
||||
Count(ctx context.Context, prefix string, opts CountOptions) (int64, error)
|
||||
|
||||
// OptimisticPut creates or updates a key-value pair if the key has not been modified or created
|
||||
// since the revision specified in expectedRevision.
|
||||
//
|
||||
// An OptimisticPut fails if the key has been modified since expectedRevision.
|
||||
OptimisticPut(ctx context.Context, key string, value []byte, expectedRevision int64, opts PutOptions) (PutResponse, error)
|
||||
|
||||
// OptimisticDelete deletes the key-value pair if it hasn't been modified since the revision
|
||||
// specified in expectedRevision.
|
||||
//
|
||||
// An OptimisticDelete fails if the key has been modified since expectedRevision.
|
||||
OptimisticDelete(ctx context.Context, key string, expectedRevision int64, opts DeleteOptions) (DeleteResponse, error)
|
||||
}
|
||||
|
||||
type GetOptions struct {
|
||||
// Revision is the point-in-time of the etcd key-value store to use for the Get operation.
|
||||
// If Revision is 0, it gets the latest value.
|
||||
Revision int64
|
||||
}
|
||||
|
||||
type ListOptions struct {
|
||||
// Revision is the point-in-time of the etcd key-value store to use for the List operation.
|
||||
// If Revision is 0, it gets the latest values.
|
||||
Revision int64
|
||||
|
||||
// Limit is the maximum number of keys to return for a List operation.
|
||||
// 0 means no limitation.
|
||||
Limit int64
|
||||
|
||||
// Continue is a key from which to resume the List operation, excluding the given key.
|
||||
// It should be set to the last key from a previous ListResponse when paginating.
|
||||
Continue string
|
||||
}
|
||||
|
||||
// CountOptions is a placeholder for potential future options for the Count operation.
|
||||
type CountOptions struct{}
|
||||
|
||||
type PutOptions struct {
|
||||
// GetOnFailure specifies whether to return the modified key-value pair if the Put operation fails due to a revision mismatch.
|
||||
GetOnFailure bool
|
||||
|
||||
// LeaseID is the ID of a lease to associate with the key allowing for automatic deletion after lease expires after it's TTL (time to live).
|
||||
// Deprecated: Should be replaced with TTL when Interface starts using one lease per object.
|
||||
LeaseID clientv3.LeaseID
|
||||
}
|
||||
|
||||
type DeleteOptions struct {
|
||||
// GetOnFailure specifies whether to return the modified key-value pair if the Delete operation fails due to a revision mismatch.
|
||||
GetOnFailure bool
|
||||
}
|
||||
|
||||
type GetResponse struct {
|
||||
// KV is the key-value pair retrieved from etcd.
|
||||
KV *mvccpb.KeyValue
|
||||
|
||||
// Revision is the revision of the key-value store at the time of the Get operation.
|
||||
Revision int64
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
// Kvs is the list of key-value pairs retrieved from etcd, ordered lexicographically by key.
|
||||
Kvs []*mvccpb.KeyValue
|
||||
|
||||
// Count is the total number of keys with the specified prefix, even if not all were returned due to a limit.
|
||||
Count int64
|
||||
|
||||
// Revision is the revision of the key-value store at the time of the List operation.
|
||||
Revision int64
|
||||
}
|
||||
|
||||
type PutResponse struct {
|
||||
// KV is the created or updated key-value pair. If the Put operation failed and GetOnFailure was true, this
|
||||
// will be the modified key-value pair that caused the failure.
|
||||
KV *mvccpb.KeyValue
|
||||
|
||||
// Succeeded indicates whether the Put operation was successful.
|
||||
Succeeded bool
|
||||
|
||||
// Revision is the revision of the key-value store after the Put operation.
|
||||
Revision int64
|
||||
}
|
||||
|
||||
type DeleteResponse struct {
|
||||
// KV is the deleted key-value pair. If the Delete operation failed and GetOnFailure was true, this
|
||||
// will be the modified key-value pair that caused the failure.
|
||||
KV *mvccpb.KeyValue
|
||||
|
||||
// Succeeded indicates whether the Delete operation was successful.
|
||||
Succeeded bool
|
||||
|
||||
// Revision is the revision of the key-value store after the Delete operation.
|
||||
Revision int64
|
||||
}
|
177
e2e/vendor/go.etcd.io/etcd/client/v3/kv.go
generated
vendored
Normal file
177
e2e/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, ContextError(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, ContextError(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, ContextError(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, ContextError(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{}, ContextError(ctx, err)
|
||||
}
|
607
e2e/vendor/go.etcd.io/etcd/client/v3/lease.go
generated
vendored
Normal file
607
e2e/vendor/go.etcd.io/etcd/client/v3/lease.go
generated
vendored
Normal file
@ -0,0 +1,607 @@
|
||||
// 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, ContextError(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, ContextError(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, ContextError(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, ContextError(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()
|
||||
|
||||
if ctx.Done() != nil {
|
||||
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, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := stream.CloseSend(); err != nil {
|
||||
if ferr == nil {
|
||||
ferr = ContextError(ctx, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
err = stream.Send(&pb.LeaseKeepAliveRequest{ID: int64(id)})
|
||||
if err != nil {
|
||||
return nil, ContextError(ctx, err)
|
||||
}
|
||||
|
||||
resp, rerr := stream.Recv()
|
||||
if rerr != nil {
|
||||
return nil, ContextError(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 ContextError(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
e2e/vendor/go.etcd.io/etcd/client/v3/logger.go
generated
vendored
Normal file
59
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/maintenance.go
generated
vendored
Normal file
255
e2e/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, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
ret := AlarmResponse{}
|
||||
for _, am := range ar.Alarms {
|
||||
dresp, derr := m.AlarmDisarm(ctx, (*AlarmMember)(am))
|
||||
if derr != nil {
|
||||
return nil, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
|
||||
func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
|
||||
remote, cancel, err := m.dial(endpoint)
|
||||
if err != nil {
|
||||
return nil, ContextError(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.Defragment(ctx, &pb.DefragmentRequest{}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.Status(ctx, &pb.StatusRequest{}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, ContextError(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, ContextError(ctx, err)
|
||||
}
|
||||
defer cancel()
|
||||
resp, err := remote.HashKV(ctx, &pb.HashKVRequest{Revision: rev}, m.callOpts...)
|
||||
if err != nil {
|
||||
return nil, ContextError(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, ContextError(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, ContextError(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), ContextError(ctx, err)
|
||||
}
|
583
e2e/vendor/go.etcd.io/etcd/client/v3/op.go
generated
vendored
Normal file
583
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/options.go
generated
vendored
Normal file
69
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/retry.go
generated
vendored
Normal file
306
e2e/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...)
|
||||
}
|
445
e2e/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go
generated
vendored
Normal file
445
e2e/vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go
generated
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
// 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"
|
||||
"errors"
|
||||
"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, 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, 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(c *Client, err error, callOpts *options) bool {
|
||||
if isContextError(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Situation when learner refuses RPC it is supposed to not serve is from the server
|
||||
// perspective not retryable.
|
||||
// But for backward-compatibility reasons we need to support situation that
|
||||
// customer provides mix of learners (not yet voters) and voters with an
|
||||
// expectation to pick voter in the next attempt.
|
||||
// TODO: Ideally client should be 'aware' which endpoint represents: leader/voter/learner with high probability.
|
||||
if errors.Is(err, rpctypes.ErrGPRCNotSupportedForLearner) && len(c.Endpoints()) > 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
switch callOpts.retryPolicy {
|
||||
case repeatable:
|
||||
return isSafeRetryImmutableRPC(err)
|
||||
case nonRepeatable:
|
||||
return isSafeRetryMutableRPC(err)
|
||||
default:
|
||||
c.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
e2e/vendor/go.etcd.io/etcd/client/v3/sort.go
generated
vendored
Normal file
37
e2e/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
|
||||
}
|
150
e2e/vendor/go.etcd.io/etcd/client/v3/txn.go
generated
vendored
Normal file
150
e2e/vendor/go.etcd.io/etcd/client/v3/txn.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
// 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, ContextError(txn.ctx, err)
|
||||
}
|
||||
return (*TxnResponse)(resp), nil
|
||||
}
|
31
e2e/vendor/go.etcd.io/etcd/client/v3/utils.go
generated
vendored
Normal file
31
e2e/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
e2e/vendor/go.etcd.io/etcd/client/v3/watch.go
generated
vendored
Normal file
1042
e2e/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