Update ingress godeps
This commit is contained in:
parent
d43021b3f1
commit
28db8fb16d
1068 changed files with 461467 additions and 117300 deletions
1285
Godeps/Godeps.json
generated
1285
Godeps/Godeps.json
generated
File diff suppressed because it is too large
Load diff
13
vendor/bitbucket.org/ww/goautoneg/Makefile
generated
vendored
Normal file
13
vendor/bitbucket.org/ww/goautoneg/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
include $(GOROOT)/src/Make.inc
|
||||||
|
|
||||||
|
TARG=bitbucket.org/ww/goautoneg
|
||||||
|
GOFILES=autoneg.go
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.pkg
|
||||||
|
|
||||||
|
format:
|
||||||
|
gofmt -w *.go
|
||||||
|
|
||||||
|
docs:
|
||||||
|
gomake clean
|
||||||
|
godoc ${TARG} > README.txt
|
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
|
@ -1 +0,0 @@
|
||||||
logrus
|
|
10
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
10
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- tip
|
|
||||||
install:
|
|
||||||
- go get github.com/stretchr/testify
|
|
||||||
- go get github.com/stvp/go-udp-testing
|
|
||||||
- go get github.com/tobi/airbrake-go
|
|
||||||
- go get github.com/getsentry/raven-go
|
|
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
21
vendor/github.com/Sirupsen/logrus/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Simon Eskildsen
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
352
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
352
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
|
@ -1,352 +0,0 @@
|
||||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus)
|
|
||||||
|
|
||||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
|
||||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
|
||||||
yet stable (pre 1.0), the core API is unlikely change much but please version
|
|
||||||
control your Logrus to make sure you aren't fetching latest `master` on every
|
|
||||||
build.**
|
|
||||||
|
|
||||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
|
||||||
plain text):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
|
|
||||||
or Splunk:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
|
||||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
|
||||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
|
||||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
|
||||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
|
||||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the default `log.Formatter = new(logrus.TextFormatter)` when a TTY is not
|
|
||||||
attached, the output is compatible with the
|
|
||||||
[l2met](http://r.32k.io/l2met-introduction) format:
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2014-04-20 15:36:23.830442383 -0400 EDT" level="info" msg="A group of walrus emerges from the ocean" animal="walrus" size=10
|
|
||||||
time="2014-04-20 15:36:23.830584199 -0400 EDT" level="warning" msg="The group's number increased tremendously!" omg=true number=122
|
|
||||||
time="2014-04-20 15:36:23.830596521 -0400 EDT" level="info" msg="A giant walrus appears!" animal="walrus" size=10
|
|
||||||
time="2014-04-20 15:36:23.830611837 -0400 EDT" level="info" msg="Tremendously sized cow enters the ocean." animal="walrus" size=9
|
|
||||||
time="2014-04-20 15:36:23.830626464 -0400 EDT" level="fatal" msg="The ice breaks!" omg=true number=100
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
|
||||||
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
|
|
||||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
|
||||||
want:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/Sirupsen/logrus/hooks/airbrake"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Log as JSON instead of the default ASCII formatter.
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
|
|
||||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
|
||||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
|
||||||
log.AddHook(&logrus_airbrake.AirbrakeHook{})
|
|
||||||
|
|
||||||
// Output to stderr instead of stdout, could also be a file.
|
|
||||||
log.SetOutput(os.Stderr)
|
|
||||||
|
|
||||||
// Only log the warning severity or above.
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For more advanced usage such as logging to multiple locations from the same
|
|
||||||
application, you can also create an instance of the `logrus` Logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create a new instance of the logger. You can have any number of instances.
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// The API for setting attributes is a little different than the package level
|
|
||||||
// exported logger. See Godoc.
|
|
||||||
log.Out = os.Stderr
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fields
|
|
||||||
|
|
||||||
Logrus encourages careful, structured logging though logging fields instead of
|
|
||||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
|
||||||
to send event %s to topic %s with key %d")`, you should log the much more
|
|
||||||
discoverable:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"event": event,
|
|
||||||
"topic": topic,
|
|
||||||
"key": key,
|
|
||||||
}).Fatal("Failed to send event")
|
|
||||||
```
|
|
||||||
|
|
||||||
We've found this API forces you to think about logging in a way that produces
|
|
||||||
much more useful logging messages. We've been in countless situations where just
|
|
||||||
a single added field to a log statement that was already there would've saved us
|
|
||||||
hours. The `WithFields` call is optional.
|
|
||||||
|
|
||||||
In general, with Logrus using any of the `printf`-family functions should be
|
|
||||||
seen as a hint you should add a field, however, you can still use the
|
|
||||||
`printf`-family functions with Logrus.
|
|
||||||
|
|
||||||
#### Hooks
|
|
||||||
|
|
||||||
You can add hooks for logging levels. For example to send errors to an exception
|
|
||||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
|
||||||
multiple places simultaneously, e.g. syslog.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Not the real implementation of the Airbrake hook. Just a simple sample.
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.AddHook(new(AirbrakeHook))
|
|
||||||
}
|
|
||||||
|
|
||||||
type AirbrakeHook struct{}
|
|
||||||
|
|
||||||
// `Fire()` takes the entry that the hook is fired for. `entry.Data[]` contains
|
|
||||||
// the fields for the entry. See the Fields section of the README.
|
|
||||||
func (hook *AirbrakeHook) Fire(entry *logrus.Entry) error {
|
|
||||||
err := airbrake.Notify(entry.Data["error"].(error))
|
|
||||||
if err != nil {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"source": "airbrake",
|
|
||||||
"endpoint": airbrake.Endpoint,
|
|
||||||
}).Info("Failed to send error to Airbrake")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// `Levels()` returns a slice of `Levels` the hook is fired for.
|
|
||||||
func (hook *AirbrakeHook) Levels() []log.Level {
|
|
||||||
return []log.Level{
|
|
||||||
log.ErrorLevel,
|
|
||||||
log.FatalLevel,
|
|
||||||
log.PanicLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Logrus comes with built-in hooks. Add those, or your custom hook, in `init`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/Sirupsen/logrus/hooks/airbrake"
|
|
||||||
"github.com/Sirupsen/logrus/hooks/syslog"
|
|
||||||
"log/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.AddHook(new(logrus_airbrake.AirbrakeHook))
|
|
||||||
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to connect to local syslog daemon")
|
|
||||||
} else {
|
|
||||||
log.AddHook(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go)
|
|
||||||
Send errors to an exception tracking service compatible with the Airbrake API.
|
|
||||||
Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes.
|
|
||||||
|
|
||||||
* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go)
|
|
||||||
Send errors to the Papertrail hosted logging service via UDP.
|
|
||||||
|
|
||||||
* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go)
|
|
||||||
Send errors to remote syslog server.
|
|
||||||
Uses standard library `log/syslog` behind the scenes.
|
|
||||||
|
|
||||||
* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus)
|
|
||||||
Send errors to a channel in hipchat.
|
|
||||||
|
|
||||||
* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly)
|
|
||||||
Send logs to Loggly (https://www.loggly.com/)
|
|
||||||
|
|
||||||
#### Level logging
|
|
||||||
|
|
||||||
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.Debug("Useful debugging information.")
|
|
||||||
log.Info("Something noteworthy happened!")
|
|
||||||
log.Warn("You should probably take a look at this.")
|
|
||||||
log.Error("Something failed but I'm not quitting.")
|
|
||||||
// Calls os.Exit(1) after logging
|
|
||||||
log.Fatal("Bye.")
|
|
||||||
// Calls panic() after logging
|
|
||||||
log.Panic("I'm bailing.")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can set the logging level on a `Logger`, then it will only log entries with
|
|
||||||
that severity or anything above it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
```
|
|
||||||
|
|
||||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
|
||||||
environment if your application has that.
|
|
||||||
|
|
||||||
#### Entries
|
|
||||||
|
|
||||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
|
||||||
automatically added to all logging events:
|
|
||||||
|
|
||||||
1. `time`. The timestamp when the entry was created.
|
|
||||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
|
||||||
the `AddFields` call. E.g. `Failed to send event.`
|
|
||||||
3. `level`. The logging level. E.g. `info`.
|
|
||||||
|
|
||||||
#### Environments
|
|
||||||
|
|
||||||
Logrus has no notion of environment.
|
|
||||||
|
|
||||||
If you wish for hooks and formatters to only be used in specific environments,
|
|
||||||
you should handle that yourself. For example, if your application has a global
|
|
||||||
variable `Environment`, which is a string representation of the environment you
|
|
||||||
could do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {
|
|
||||||
// do something here to set environment depending on an environment variable
|
|
||||||
// or command-line flag
|
|
||||||
if Environment == "production" {
|
|
||||||
log.SetFormatter(logrus.JSONFormatter)
|
|
||||||
} else {
|
|
||||||
// The TextFormatter is default, you don't actually have to do this.
|
|
||||||
log.SetFormatter(logrus.TextFormatter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This configuration is how `logrus` was intended to be used, but JSON in
|
|
||||||
production is mostly only useful if you do log aggregation with tools like
|
|
||||||
Splunk or Logstash.
|
|
||||||
|
|
||||||
#### Formatters
|
|
||||||
|
|
||||||
The built-in logging formatters are:
|
|
||||||
|
|
||||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
|
||||||
without colors.
|
|
||||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
|
||||||
field to `true`. To force no colored output even if there is a TTY set the
|
|
||||||
`DisableColors` field to `true`
|
|
||||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
|
||||||
|
|
||||||
Third party logging formatters:
|
|
||||||
|
|
||||||
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
|
||||||
|
|
||||||
You can define your formatter by implementing the `Formatter` interface,
|
|
||||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
|
||||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
|
||||||
default ones (see Entries section above):
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyJSONFormatter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFormatter(new(MyJSONFormatter))
|
|
||||||
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
// Note this doesn't include Time, Level and Message which are available on
|
|
||||||
// the Entry. Consult `godoc` on information about those fields or read the
|
|
||||||
// source of the official loggers.
|
|
||||||
serialized, err := json.Marshal(entry.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Rotation
|
|
||||||
|
|
||||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
|
||||||
external program (like `logrotated(8)`) that can compress and delete old log
|
|
||||||
entries. It should not be a feature of the application-level logger.
|
|
||||||
|
|
||||||
|
|
||||||
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
|
|
248
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
248
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
|
@ -1,248 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
|
||||||
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
|
|
||||||
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
|
|
||||||
// passed around as much as you wish to avoid field duplication.
|
|
||||||
type Entry struct {
|
|
||||||
Logger *Logger
|
|
||||||
|
|
||||||
// Contains all the fields set by the user.
|
|
||||||
Data Fields
|
|
||||||
|
|
||||||
// Time at which the log entry was created
|
|
||||||
Time time.Time
|
|
||||||
|
|
||||||
// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
Level Level
|
|
||||||
|
|
||||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEntry(logger *Logger) *Entry {
|
|
||||||
return &Entry{
|
|
||||||
Logger: logger,
|
|
||||||
// Default is three fields, give a little extra room
|
|
||||||
Data: make(Fields, 5),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
|
||||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
|
||||||
return bytes.NewBuffer(serialized), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string representation from the reader and ultimately the
|
|
||||||
// formatter.
|
|
||||||
func (entry *Entry) String() (string, error) {
|
|
||||||
reader, err := entry.Reader()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return reader.String(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a single field to the Entry.
|
|
||||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
|
||||||
return entry.WithFields(Fields{key: value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a map of fields to the Entry.
|
|
||||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
|
||||||
data := Fields{}
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range fields {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: data}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) log(level Level, msg string) {
|
|
||||||
entry.Time = time.Now()
|
|
||||||
entry.Level = level
|
|
||||||
entry.Message = msg
|
|
||||||
|
|
||||||
if err := entry.Logger.Hooks.Fire(level, entry); err != nil {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := entry.Reader()
|
|
||||||
if err != nil {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
defer entry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
_, err = io.Copy(entry.Logger.Out, reader)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
|
||||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
|
||||||
// directly here.
|
|
||||||
if level <= PanicLevel {
|
|
||||||
panic(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debug(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= DebugLevel {
|
|
||||||
entry.log(DebugLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Print(args ...interface{}) {
|
|
||||||
entry.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Info(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= InfoLevel {
|
|
||||||
entry.log(InfoLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warn(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= WarnLevel {
|
|
||||||
entry.log(WarnLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Error(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= ErrorLevel {
|
|
||||||
entry.log(ErrorLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatal(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= FatalLevel {
|
|
||||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= PanicLevel {
|
|
||||||
entry.log(PanicLevel, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
panic(fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Printf family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= DebugLevel {
|
|
||||||
entry.Debug(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= InfoLevel {
|
|
||||||
entry.Info(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
|
||||||
entry.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= WarnLevel {
|
|
||||||
entry.Warn(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
|
||||||
entry.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= ErrorLevel {
|
|
||||||
entry.Error(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= FatalLevel {
|
|
||||||
entry.Fatal(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= PanicLevel {
|
|
||||||
entry.Panic(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Println family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Debugln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= DebugLevel {
|
|
||||||
entry.Debug(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infoln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= InfoLevel {
|
|
||||||
entry.Info(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Println(args ...interface{}) {
|
|
||||||
entry.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= WarnLevel {
|
|
||||||
entry.Warn(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningln(args ...interface{}) {
|
|
||||||
entry.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= ErrorLevel {
|
|
||||||
entry.Error(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= FatalLevel {
|
|
||||||
entry.Fatal(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicln(args ...interface{}) {
|
|
||||||
if entry.Logger.Level >= PanicLevel {
|
|
||||||
entry.Panic(entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
|
||||||
// fmt.Sprintln where spaces are always added between operands, regardless of
|
|
||||||
// their type. Instead of vendoring the Sprintln implementation to spare a
|
|
||||||
// string allocation, we do the simplest thing.
|
|
||||||
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
|
||||||
msg := fmt.Sprintln(args...)
|
|
||||||
return msg[:len(msg)-1]
|
|
||||||
}
|
|
182
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
182
vendor/github.com/Sirupsen/logrus/exported.go
generated
vendored
|
@ -1,182 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// std is the name of the standard logger in stdlib `log`
|
|
||||||
std = New()
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetOutput sets the standard logger output.
|
|
||||||
func SetOutput(out io.Writer) {
|
|
||||||
std.mu.Lock()
|
|
||||||
defer std.mu.Unlock()
|
|
||||||
std.Out = out
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFormatter sets the standard logger formatter.
|
|
||||||
func SetFormatter(formatter Formatter) {
|
|
||||||
std.mu.Lock()
|
|
||||||
defer std.mu.Unlock()
|
|
||||||
std.Formatter = formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the standard logger level.
|
|
||||||
func SetLevel(level Level) {
|
|
||||||
std.mu.Lock()
|
|
||||||
defer std.mu.Unlock()
|
|
||||||
std.Level = level
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the standard logger level.
|
|
||||||
func GetLevel() Level {
|
|
||||||
return std.Level
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHook adds a hook to the standard logger hooks.
|
|
||||||
func AddHook(hook Hook) {
|
|
||||||
std.mu.Lock()
|
|
||||||
defer std.mu.Unlock()
|
|
||||||
std.Hooks.Add(hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithField creates an entry from the standard logger and adds a field to
|
|
||||||
// it. If you want multiple fields, use `WithFields`.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithField(key string, value interface{}) *Entry {
|
|
||||||
return std.WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFields creates an entry from the standard logger and adds multiple
|
|
||||||
// fields to it. This is simply a helper for `WithField`, invoking it
|
|
||||||
// once for each field.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithFields(fields Fields) *Entry {
|
|
||||||
return std.WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug logs a message at level Debug on the standard logger.
|
|
||||||
func Debug(args ...interface{}) {
|
|
||||||
std.Debug(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print logs a message at level Info on the standard logger.
|
|
||||||
func Print(args ...interface{}) {
|
|
||||||
std.Print(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs a message at level Info on the standard logger.
|
|
||||||
func Info(args ...interface{}) {
|
|
||||||
std.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn logs a message at level Warn on the standard logger.
|
|
||||||
func Warn(args ...interface{}) {
|
|
||||||
std.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning logs a message at level Warn on the standard logger.
|
|
||||||
func Warning(args ...interface{}) {
|
|
||||||
std.Warning(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs a message at level Error on the standard logger.
|
|
||||||
func Error(args ...interface{}) {
|
|
||||||
std.Error(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic logs a message at level Panic on the standard logger.
|
|
||||||
func Panic(args ...interface{}) {
|
|
||||||
std.Panic(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal logs a message at level Fatal on the standard logger.
|
|
||||||
func Fatal(args ...interface{}) {
|
|
||||||
std.Fatal(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugf logs a message at level Debug on the standard logger.
|
|
||||||
func Debugf(format string, args ...interface{}) {
|
|
||||||
std.Debugf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf logs a message at level Info on the standard logger.
|
|
||||||
func Printf(format string, args ...interface{}) {
|
|
||||||
std.Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof logs a message at level Info on the standard logger.
|
|
||||||
func Infof(format string, args ...interface{}) {
|
|
||||||
std.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnf logs a message at level Warn on the standard logger.
|
|
||||||
func Warnf(format string, args ...interface{}) {
|
|
||||||
std.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningf logs a message at level Warn on the standard logger.
|
|
||||||
func Warningf(format string, args ...interface{}) {
|
|
||||||
std.Warningf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf logs a message at level Error on the standard logger.
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
|
||||||
std.Errorf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicf logs a message at level Panic on the standard logger.
|
|
||||||
func Panicf(format string, args ...interface{}) {
|
|
||||||
std.Panicf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf logs a message at level Fatal on the standard logger.
|
|
||||||
func Fatalf(format string, args ...interface{}) {
|
|
||||||
std.Fatalf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugln logs a message at level Debug on the standard logger.
|
|
||||||
func Debugln(args ...interface{}) {
|
|
||||||
std.Debugln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println logs a message at level Info on the standard logger.
|
|
||||||
func Println(args ...interface{}) {
|
|
||||||
std.Println(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infoln logs a message at level Info on the standard logger.
|
|
||||||
func Infoln(args ...interface{}) {
|
|
||||||
std.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnln logs a message at level Warn on the standard logger.
|
|
||||||
func Warnln(args ...interface{}) {
|
|
||||||
std.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningln logs a message at level Warn on the standard logger.
|
|
||||||
func Warningln(args ...interface{}) {
|
|
||||||
std.Warningln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorln logs a message at level Error on the standard logger.
|
|
||||||
func Errorln(args ...interface{}) {
|
|
||||||
std.Errorln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicln logs a message at level Panic on the standard logger.
|
|
||||||
func Panicln(args ...interface{}) {
|
|
||||||
std.Panicln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalln logs a message at level Fatal on the standard logger.
|
|
||||||
func Fatalln(args ...interface{}) {
|
|
||||||
std.Fatalln(args...)
|
|
||||||
}
|
|
44
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
44
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
|
@ -1,44 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
|
||||||
// `Entry`. It exposes all the fields, including the default ones:
|
|
||||||
//
|
|
||||||
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
|
||||||
// * `entry.Data["time"]`. The timestamp.
|
|
||||||
// * `entry.Data["level"]. The level the entry was logged at.
|
|
||||||
//
|
|
||||||
// Any additional fields added with `WithField` or `WithFields` are also in
|
|
||||||
// `entry.Data`. Format is expected to return an array of bytes which are then
|
|
||||||
// logged to `logger.Out`.
|
|
||||||
type Formatter interface {
|
|
||||||
Format(*Entry) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is to not silently overwrite `time`, `msg` and `level` fields when
|
|
||||||
// dumping it. If this code wasn't there doing:
|
|
||||||
//
|
|
||||||
// logrus.WithField("level", 1).Info("hello")
|
|
||||||
//
|
|
||||||
// Would just silently drop the user provided level. Instead with this code
|
|
||||||
// it'll logged as:
|
|
||||||
//
|
|
||||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
|
||||||
//
|
|
||||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
|
||||||
// avoid code duplication between the two default formatters.
|
|
||||||
func prefixFieldClashes(data Fields) {
|
|
||||||
_, ok := data["time"]
|
|
||||||
if ok {
|
|
||||||
data["fields.time"] = data["time"]
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok = data["msg"]
|
|
||||||
if ok {
|
|
||||||
data["fields.msg"] = data["msg"]
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok = data["level"]
|
|
||||||
if ok {
|
|
||||||
data["fields.level"] = data["level"]
|
|
||||||
}
|
|
||||||
}
|
|
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
34
vendor/github.com/Sirupsen/logrus/hooks.go
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
// A hook to be fired when logging on the logging levels returned from
|
|
||||||
// `Levels()` on your implementation of the interface. Note that this is not
|
|
||||||
// fired in a goroutine or a channel with workers, you should handle such
|
|
||||||
// functionality yourself if your call is non-blocking and you don't wish for
|
|
||||||
// the logging calls for levels returned from `Levels()` to block.
|
|
||||||
type Hook interface {
|
|
||||||
Levels() []Level
|
|
||||||
Fire(*Entry) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal type for storing the hooks on a logger instance.
|
|
||||||
type levelHooks map[Level][]Hook
|
|
||||||
|
|
||||||
// Add a hook to an instance of logger. This is called with
|
|
||||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
|
||||||
func (hooks levelHooks) Add(hook Hook) {
|
|
||||||
for _, level := range hook.Levels() {
|
|
||||||
hooks[level] = append(hooks[level], hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
|
||||||
// appropriate hooks for a log entry.
|
|
||||||
func (hooks levelHooks) Fire(level Level, entry *Entry) error {
|
|
||||||
for _, hook := range hooks[level] {
|
|
||||||
if err := hook.Fire(entry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
26
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
26
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
|
@ -1,26 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JSONFormatter struct{}
|
|
||||||
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
data := make(Fields, len(entry.Data)+3)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
prefixFieldClashes(data)
|
|
||||||
data["time"] = entry.Time.Format(time.RFC3339)
|
|
||||||
data["msg"] = entry.Message
|
|
||||||
data["level"] = entry.Level.String()
|
|
||||||
|
|
||||||
serialized, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
161
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
161
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
|
@ -1,161 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Logger struct {
|
|
||||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
|
||||||
// file, or leave it default which is `os.Stdout`. You can also set this to
|
|
||||||
// something more adventorous, such as logging to Kafka.
|
|
||||||
Out io.Writer
|
|
||||||
// Hooks for the logger instance. These allow firing events based on logging
|
|
||||||
// levels and log entries. For example, to send errors to an error tracking
|
|
||||||
// service, log to StatsD or dump the core on fatal errors.
|
|
||||||
Hooks levelHooks
|
|
||||||
// All log entries pass through the formatter before logged to Out. The
|
|
||||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
|
||||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
|
||||||
// logs with colors, but to a file it wouldn't. You can easily implement your
|
|
||||||
// own that implements the `Formatter` interface, see the `README` or included
|
|
||||||
// formatters for examples.
|
|
||||||
Formatter Formatter
|
|
||||||
// The logging level the logger should log at. This is typically (and defaults
|
|
||||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
|
||||||
// logged. `logrus.Debug` is useful in
|
|
||||||
Level Level
|
|
||||||
// Used to sync writing to the log.
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
|
||||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
|
||||||
// instantiate your own:
|
|
||||||
//
|
|
||||||
// var log = &Logger{
|
|
||||||
// Out: os.Stderr,
|
|
||||||
// Formatter: new(JSONFormatter),
|
|
||||||
// Hooks: make(levelHooks),
|
|
||||||
// Level: logrus.DebugLevel,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// It's recommended to make this a global instance called `log`.
|
|
||||||
func New() *Logger {
|
|
||||||
return &Logger{
|
|
||||||
Out: os.Stdout,
|
|
||||||
Formatter: new(TextFormatter),
|
|
||||||
Hooks: make(levelHooks),
|
|
||||||
Level: InfoLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
|
||||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
|
||||||
// Ff you want multiple fields, use `WithFields`.
|
|
||||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
|
||||||
return NewEntry(logger).WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
|
||||||
// each `Field`.
|
|
||||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
|
||||||
return NewEntry(logger).WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Debugf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Errorf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Fatalf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
|
||||||
NewEntry(logger).Panicf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debug(args ...interface{}) {
|
|
||||||
NewEntry(logger).Debug(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Info(args ...interface{}) {
|
|
||||||
NewEntry(logger).Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Print(args ...interface{}) {
|
|
||||||
NewEntry(logger).Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warn(args ...interface{}) {
|
|
||||||
NewEntry(logger).Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warning(args ...interface{}) {
|
|
||||||
NewEntry(logger).Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Error(args ...interface{}) {
|
|
||||||
NewEntry(logger).Error(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatal(args ...interface{}) {
|
|
||||||
NewEntry(logger).Fatal(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panic(args ...interface{}) {
|
|
||||||
NewEntry(logger).Panic(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Debugln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infoln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Println(args ...interface{}) {
|
|
||||||
NewEntry(logger).Println(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Errorln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Fatalln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicln(args ...interface{}) {
|
|
||||||
NewEntry(logger).Panicln(args...)
|
|
||||||
}
|
|
94
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
94
vendor/github.com/Sirupsen/logrus/logrus.go
generated
vendored
|
@ -1,94 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fields type, used to pass to `WithFields`.
|
|
||||||
type Fields map[string]interface{}
|
|
||||||
|
|
||||||
// Level type
|
|
||||||
type Level uint8
|
|
||||||
|
|
||||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
|
||||||
func (level Level) String() string {
|
|
||||||
switch level {
|
|
||||||
case DebugLevel:
|
|
||||||
return "debug"
|
|
||||||
case InfoLevel:
|
|
||||||
return "info"
|
|
||||||
case WarnLevel:
|
|
||||||
return "warning"
|
|
||||||
case ErrorLevel:
|
|
||||||
return "error"
|
|
||||||
case FatalLevel:
|
|
||||||
return "fatal"
|
|
||||||
case PanicLevel:
|
|
||||||
return "panic"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
|
||||||
func ParseLevel(lvl string) (Level, error) {
|
|
||||||
switch lvl {
|
|
||||||
case "panic":
|
|
||||||
return PanicLevel, nil
|
|
||||||
case "fatal":
|
|
||||||
return FatalLevel, nil
|
|
||||||
case "error":
|
|
||||||
return ErrorLevel, nil
|
|
||||||
case "warn", "warning":
|
|
||||||
return WarnLevel, nil
|
|
||||||
case "info":
|
|
||||||
return InfoLevel, nil
|
|
||||||
case "debug":
|
|
||||||
return DebugLevel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var l Level
|
|
||||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are the different logging levels. You can set the logging level to log
|
|
||||||
// on your instance of logger, obtained with `logrus.New()`.
|
|
||||||
const (
|
|
||||||
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
|
||||||
// message passed to Debug, Info, ...
|
|
||||||
PanicLevel Level = iota
|
|
||||||
// FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the
|
|
||||||
// logging level is set to Panic.
|
|
||||||
FatalLevel
|
|
||||||
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
|
||||||
// Commonly used for hooks to send errors to an error tracking service.
|
|
||||||
ErrorLevel
|
|
||||||
// WarnLevel level. Non-critical entries that deserve eyes.
|
|
||||||
WarnLevel
|
|
||||||
// InfoLevel level. General operational entries about what's going on inside the
|
|
||||||
// application.
|
|
||||||
InfoLevel
|
|
||||||
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
|
||||||
DebugLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
|
||||||
var _ StdLogger = &log.Logger{}
|
|
||||||
|
|
||||||
// StdLogger is what your logrus-enabled library should take, that way
|
|
||||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
|
||||||
// interface, this is the closest we get, unfortunately.
|
|
||||||
type StdLogger interface {
|
|
||||||
Print(...interface{})
|
|
||||||
Printf(string, ...interface{})
|
|
||||||
Println(...interface{})
|
|
||||||
|
|
||||||
Fatal(...interface{})
|
|
||||||
Fatalf(string, ...interface{})
|
|
||||||
Fatalln(...interface{})
|
|
||||||
|
|
||||||
Panic(...interface{})
|
|
||||||
Panicf(string, ...interface{})
|
|
||||||
Panicln(...interface{})
|
|
||||||
}
|
|
20
vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
generated
vendored
20
vendor/github.com/Sirupsen/logrus/terminal_freebsd.go
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
Go 1.2 doesn't include Termios for FreeBSD. This should be added in 1.3 and this could be merged with terminal_darwin.
|
|
||||||
*/
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ioctlReadTermios = syscall.TIOCGETA
|
|
||||||
|
|
||||||
type Termios struct {
|
|
||||||
Iflag uint32
|
|
||||||
Oflag uint32
|
|
||||||
Cflag uint32
|
|
||||||
Lflag uint32
|
|
||||||
Cc [20]uint8
|
|
||||||
Ispeed uint32
|
|
||||||
Ospeed uint32
|
|
||||||
}
|
|
21
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
21
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
// Based on ssh/terminal:
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build linux,!appengine darwin freebsd
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal() bool {
|
|
||||||
fd := syscall.Stdout
|
|
||||||
var termios Termios
|
|
||||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
|
||||||
return err == 0
|
|
||||||
}
|
|
27
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
27
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
// Based on ssh/terminal:
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
||||||
|
|
||||||
var (
|
|
||||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal() bool {
|
|
||||||
fd := syscall.Stdout
|
|
||||||
var st uint32
|
|
||||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
|
||||||
return r != 0 && e == 0
|
|
||||||
}
|
|
124
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
124
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
|
@ -1,124 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
nocolor = 0
|
|
||||||
red = 31
|
|
||||||
green = 32
|
|
||||||
yellow = 33
|
|
||||||
blue = 34
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
baseTimestamp time.Time
|
|
||||||
isTerminal bool
|
|
||||||
noQuoteNeeded *regexp.Regexp
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
baseTimestamp = time.Now()
|
|
||||||
isTerminal = IsTerminal()
|
|
||||||
}
|
|
||||||
|
|
||||||
func miniTS() int {
|
|
||||||
return int(time.Since(baseTimestamp) / time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
type TextFormatter struct {
|
|
||||||
// Set to true to bypass checking for a TTY before outputting colors.
|
|
||||||
ForceColors bool
|
|
||||||
DisableColors bool
|
|
||||||
// Set to true to disable timestamp logging (useful when the output
|
|
||||||
// is redirected to a logging system already adding a timestamp)
|
|
||||||
DisableTimestamp bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
|
|
||||||
var keys []string
|
|
||||||
for k := range entry.Data {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
|
||||||
|
|
||||||
prefixFieldClashes(entry.Data)
|
|
||||||
|
|
||||||
isColored := (f.ForceColors || isTerminal) && !f.DisableColors
|
|
||||||
|
|
||||||
if isColored {
|
|
||||||
printColored(b, entry, keys)
|
|
||||||
} else {
|
|
||||||
if !f.DisableTimestamp {
|
|
||||||
f.appendKeyValue(b, "time", entry.Time.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
f.appendKeyValue(b, "level", entry.Level.String())
|
|
||||||
f.appendKeyValue(b, "msg", entry.Message)
|
|
||||||
for _, key := range keys {
|
|
||||||
f.appendKeyValue(b, key, entry.Data[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteByte('\n')
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printColored(b *bytes.Buffer, entry *Entry, keys []string) {
|
|
||||||
var levelColor int
|
|
||||||
switch entry.Level {
|
|
||||||
case WarnLevel:
|
|
||||||
levelColor = yellow
|
|
||||||
case ErrorLevel, FatalLevel, PanicLevel:
|
|
||||||
levelColor = red
|
|
||||||
default:
|
|
||||||
levelColor = blue
|
|
||||||
}
|
|
||||||
|
|
||||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
|
||||||
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
|
||||||
for _, k := range keys {
|
|
||||||
v := entry.Data[k]
|
|
||||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func needsQuoting(text string) bool {
|
|
||||||
for _, ch := range text {
|
|
||||||
if !((ch >= 'a' && ch <= 'z') ||
|
|
||||||
(ch >= 'A' && ch <= 'Z') ||
|
|
||||||
(ch >= '0' && ch < '9') ||
|
|
||||||
ch == '-' || ch == '.') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key, value interface{}) {
|
|
||||||
switch value.(type) {
|
|
||||||
case string:
|
|
||||||
if needsQuoting(value.(string)) {
|
|
||||||
fmt.Fprintf(b, "%v=%s ", key, value)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
|
||||||
}
|
|
||||||
case error:
|
|
||||||
if needsQuoting(value.(error).Error()) {
|
|
||||||
fmt.Fprintf(b, "%v=%s ", key, value)
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(b, "%v=%q ", key, value)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(b, "%v=%v ", key, value)
|
|
||||||
}
|
|
||||||
}
|
|
13
vendor/github.com/opencontainers/runc/LICENSE → vendor/github.com/coreos/etcd/LICENSE
generated
vendored
13
vendor/github.com/opencontainers/runc/LICENSE → vendor/github.com/coreos/etcd/LICENSE
generated
vendored
|
@ -176,7 +176,18 @@
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
Copyright 2014 Docker, Inc.
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
5
vendor/github.com/coreos/etcd/NOTICE
generated
vendored
Normal file
5
vendor/github.com/coreos/etcd/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
CoreOS Project
|
||||||
|
Copyright 2014 CoreOS, Inc
|
||||||
|
|
||||||
|
This product includes software developed at CoreOS, Inc.
|
||||||
|
(http://www.coreos.com/).
|
820
vendor/github.com/coreos/etcd/auth/authpb/auth.pb.go
generated
vendored
Normal file
820
vendor/github.com/coreos/etcd/auth/authpb/auth.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,820 @@
|
||||||
|
// Code generated by protoc-gen-gogo.
|
||||||
|
// source: auth.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package authpb is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
auth.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
User
|
||||||
|
Permission
|
||||||
|
Role
|
||||||
|
*/
|
||||||
|
package authpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
const _ = proto.ProtoPackageIsVersion1
|
||||||
|
|
||||||
|
type Permission_Type int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
READ Permission_Type = 0
|
||||||
|
WRITE Permission_Type = 1
|
||||||
|
READWRITE Permission_Type = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var Permission_Type_name = map[int32]string{
|
||||||
|
0: "READ",
|
||||||
|
1: "WRITE",
|
||||||
|
2: "READWRITE",
|
||||||
|
}
|
||||||
|
var Permission_Type_value = map[string]int32{
|
||||||
|
"READ": 0,
|
||||||
|
"WRITE": 1,
|
||||||
|
"READWRITE": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Permission_Type) String() string {
|
||||||
|
return proto.EnumName(Permission_Type_name, int32(x))
|
||||||
|
}
|
||||||
|
func (Permission_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptorAuth, []int{1, 0} }
|
||||||
|
|
||||||
|
// User is a single entry in the bucket authUsers
|
||||||
|
type User struct {
|
||||||
|
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
Password []byte `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
|
||||||
|
Roles []string `protobuf:"bytes,3,rep,name=roles" json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *User) Reset() { *m = User{} }
|
||||||
|
func (m *User) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*User) ProtoMessage() {}
|
||||||
|
func (*User) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{0} }
|
||||||
|
|
||||||
|
// Permission is a single entity
|
||||||
|
type Permission struct {
|
||||||
|
PermType Permission_Type `protobuf:"varint,1,opt,name=permType,proto3,enum=authpb.Permission_Type" json:"permType,omitempty"`
|
||||||
|
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
RangeEnd []byte `protobuf:"bytes,3,opt,name=range_end,json=rangeEnd,proto3" json:"range_end,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Permission) Reset() { *m = Permission{} }
|
||||||
|
func (m *Permission) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Permission) ProtoMessage() {}
|
||||||
|
func (*Permission) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{1} }
|
||||||
|
|
||||||
|
// Role is a single entry in the bucket authRoles
|
||||||
|
type Role struct {
|
||||||
|
Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
KeyPermission []*Permission `protobuf:"bytes,2,rep,name=keyPermission" json:"keyPermission,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Role) Reset() { *m = Role{} }
|
||||||
|
func (m *Role) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Role) ProtoMessage() {}
|
||||||
|
func (*Role) Descriptor() ([]byte, []int) { return fileDescriptorAuth, []int{2} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*User)(nil), "authpb.User")
|
||||||
|
proto.RegisterType((*Permission)(nil), "authpb.Permission")
|
||||||
|
proto.RegisterType((*Role)(nil), "authpb.Role")
|
||||||
|
proto.RegisterEnum("authpb.Permission_Type", Permission_Type_name, Permission_Type_value)
|
||||||
|
}
|
||||||
|
func (m *User) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *User) MarshalTo(data []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Name) > 0 {
|
||||||
|
data[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(len(m.Name)))
|
||||||
|
i += copy(data[i:], m.Name)
|
||||||
|
}
|
||||||
|
if len(m.Password) > 0 {
|
||||||
|
data[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(len(m.Password)))
|
||||||
|
i += copy(data[i:], m.Password)
|
||||||
|
}
|
||||||
|
if len(m.Roles) > 0 {
|
||||||
|
for _, s := range m.Roles {
|
||||||
|
data[i] = 0x1a
|
||||||
|
i++
|
||||||
|
l = len(s)
|
||||||
|
for l >= 1<<7 {
|
||||||
|
data[i] = uint8(uint64(l)&0x7f | 0x80)
|
||||||
|
l >>= 7
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
data[i] = uint8(l)
|
||||||
|
i++
|
||||||
|
i += copy(data[i:], s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Permission) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Permission) MarshalTo(data []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.PermType != 0 {
|
||||||
|
data[i] = 0x8
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(m.PermType))
|
||||||
|
}
|
||||||
|
if len(m.Key) > 0 {
|
||||||
|
data[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(len(m.Key)))
|
||||||
|
i += copy(data[i:], m.Key)
|
||||||
|
}
|
||||||
|
if len(m.RangeEnd) > 0 {
|
||||||
|
data[i] = 0x1a
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(len(m.RangeEnd)))
|
||||||
|
i += copy(data[i:], m.RangeEnd)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Role) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Role) MarshalTo(data []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Name) > 0 {
|
||||||
|
data[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(len(m.Name)))
|
||||||
|
i += copy(data[i:], m.Name)
|
||||||
|
}
|
||||||
|
if len(m.KeyPermission) > 0 {
|
||||||
|
for _, msg := range m.KeyPermission {
|
||||||
|
data[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintAuth(data, i, uint64(msg.Size()))
|
||||||
|
n, err := msg.MarshalTo(data[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFixed64Auth(data []byte, offset int, v uint64) int {
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
data[offset+1] = uint8(v >> 8)
|
||||||
|
data[offset+2] = uint8(v >> 16)
|
||||||
|
data[offset+3] = uint8(v >> 24)
|
||||||
|
data[offset+4] = uint8(v >> 32)
|
||||||
|
data[offset+5] = uint8(v >> 40)
|
||||||
|
data[offset+6] = uint8(v >> 48)
|
||||||
|
data[offset+7] = uint8(v >> 56)
|
||||||
|
return offset + 8
|
||||||
|
}
|
||||||
|
func encodeFixed32Auth(data []byte, offset int, v uint32) int {
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
data[offset+1] = uint8(v >> 8)
|
||||||
|
data[offset+2] = uint8(v >> 16)
|
||||||
|
data[offset+3] = uint8(v >> 24)
|
||||||
|
return offset + 4
|
||||||
|
}
|
||||||
|
func encodeVarintAuth(data []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
data[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *User) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Name)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Password)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
if len(m.Roles) > 0 {
|
||||||
|
for _, s := range m.Roles {
|
||||||
|
l = len(s)
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Permission) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.PermType != 0 {
|
||||||
|
n += 1 + sovAuth(uint64(m.PermType))
|
||||||
|
}
|
||||||
|
l = len(m.Key)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.RangeEnd)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Role) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Name)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
if len(m.KeyPermission) > 0 {
|
||||||
|
for _, e := range m.KeyPermission {
|
||||||
|
l = e.Size()
|
||||||
|
n += 1 + l + sovAuth(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovAuth(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozAuth(x uint64) (n int) {
|
||||||
|
return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (m *User) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: User: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: User: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Name = append(m.Name[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Name == nil {
|
||||||
|
m.Name = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Password = append(m.Password[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Password == nil {
|
||||||
|
m.Password = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType)
|
||||||
|
}
|
||||||
|
var stringLen uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
stringLen |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
intStringLen := int(stringLen)
|
||||||
|
if intStringLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + intStringLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Roles = append(m.Roles, string(data[iNdEx:postIndex]))
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipAuth(data[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Permission) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Permission: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Permission: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field PermType", wireType)
|
||||||
|
}
|
||||||
|
m.PermType = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.PermType |= (Permission_Type(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Key = append(m.Key[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Key == nil {
|
||||||
|
m.Key = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.RangeEnd = append(m.RangeEnd[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.RangeEnd == nil {
|
||||||
|
m.RangeEnd = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipAuth(data[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Role) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Role: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Role: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Name = append(m.Name[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Name == nil {
|
||||||
|
m.Name = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field KeyPermission", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.KeyPermission = append(m.KeyPermission, &Permission{})
|
||||||
|
if err := m.KeyPermission[len(m.KeyPermission)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipAuth(data[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipAuth(data []byte) (n int, err error) {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if data[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthAuth
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowAuth
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipAuth(data[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
var fileDescriptorAuth = []byte{
|
||||||
|
// 288 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x90, 0xc1, 0x4a, 0xc3, 0x30,
|
||||||
|
0x1c, 0xc6, 0x9b, 0xb6, 0x1b, 0xed, 0x5f, 0x27, 0x25, 0x0c, 0x0c, 0x13, 0x42, 0xe9, 0xa9, 0x78,
|
||||||
|
0xa8, 0xb0, 0x5d, 0xbc, 0x2a, 0xf6, 0x20, 0x78, 0x90, 0x50, 0xf1, 0x28, 0x1d, 0x0d, 0x75, 0x6c,
|
||||||
|
0x6d, 0x4a, 0x32, 0x91, 0xbe, 0x89, 0x07, 0x1f, 0x68, 0xc7, 0x3d, 0x82, 0xab, 0x2f, 0x22, 0x4d,
|
||||||
|
0x64, 0x43, 0xdc, 0xed, 0xfb, 0xbe, 0xff, 0x97, 0xe4, 0x97, 0x3f, 0x40, 0xfe, 0xb6, 0x7e, 0x4d,
|
||||||
|
0x1a, 0x29, 0xd6, 0x02, 0x0f, 0x7b, 0xdd, 0xcc, 0x27, 0xe3, 0x52, 0x94, 0x42, 0x47, 0x57, 0xbd,
|
||||||
|
0x32, 0xd3, 0xe8, 0x01, 0xdc, 0x27, 0xc5, 0x25, 0xc6, 0xe0, 0xd6, 0x79, 0xc5, 0x09, 0x0a, 0x51,
|
||||||
|
0x7c, 0xca, 0xb4, 0xc6, 0x13, 0xf0, 0x9a, 0x5c, 0xa9, 0x77, 0x21, 0x0b, 0x62, 0xeb, 0x7c, 0xef,
|
||||||
|
0xf1, 0x18, 0x06, 0x52, 0xac, 0xb8, 0x22, 0x4e, 0xe8, 0xc4, 0x3e, 0x33, 0x26, 0xfa, 0x44, 0x00,
|
||||||
|
0x8f, 0x5c, 0x56, 0x0b, 0xa5, 0x16, 0xa2, 0xc6, 0x33, 0xf0, 0x1a, 0x2e, 0xab, 0xac, 0x6d, 0xcc,
|
||||||
|
0xc5, 0x67, 0xd3, 0xf3, 0xc4, 0xd0, 0x24, 0x87, 0x56, 0xd2, 0x8f, 0xd9, 0xbe, 0x88, 0x03, 0x70,
|
||||||
|
0x96, 0xbc, 0xfd, 0x7d, 0xb0, 0x97, 0xf8, 0x02, 0x7c, 0x99, 0xd7, 0x25, 0x7f, 0xe1, 0x75, 0x41,
|
||||||
|
0x1c, 0x03, 0xa2, 0x83, 0xb4, 0x2e, 0xa2, 0x4b, 0x70, 0xf5, 0x31, 0x0f, 0x5c, 0x96, 0xde, 0xdc,
|
||||||
|
0x05, 0x16, 0xf6, 0x61, 0xf0, 0xcc, 0xee, 0xb3, 0x34, 0x40, 0x78, 0x04, 0x7e, 0x1f, 0x1a, 0x6b,
|
||||||
|
0x47, 0x19, 0xb8, 0x4c, 0xac, 0xf8, 0xd1, 0xcf, 0x5e, 0xc3, 0x68, 0xc9, 0xdb, 0x03, 0x16, 0xb1,
|
||||||
|
0x43, 0x27, 0x3e, 0x99, 0xe2, 0xff, 0xc0, 0xec, 0x6f, 0xf1, 0x96, 0x6c, 0x76, 0xd4, 0xda, 0xee,
|
||||||
|
0xa8, 0xb5, 0xe9, 0x28, 0xda, 0x76, 0x14, 0x7d, 0x75, 0x14, 0x7d, 0x7c, 0x53, 0x6b, 0x3e, 0xd4,
|
||||||
|
0x3b, 0x9e, 0xfd, 0x04, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x76, 0x8d, 0x4f, 0x8f, 0x01, 0x00, 0x00,
|
||||||
|
}
|
37
vendor/github.com/coreos/etcd/auth/authpb/auth.proto
generated
vendored
Normal file
37
vendor/github.com/coreos/etcd/auth/authpb/auth.proto
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package authpb;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option (gogoproto.marshaler_all) = true;
|
||||||
|
option (gogoproto.sizer_all) = true;
|
||||||
|
option (gogoproto.unmarshaler_all) = true;
|
||||||
|
option (gogoproto.goproto_getters_all) = false;
|
||||||
|
option (gogoproto.goproto_enum_prefix_all) = false;
|
||||||
|
|
||||||
|
// User is a single entry in the bucket authUsers
|
||||||
|
message User {
|
||||||
|
bytes name = 1;
|
||||||
|
bytes password = 2;
|
||||||
|
repeated string roles = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permission is a single entity
|
||||||
|
message Permission {
|
||||||
|
enum Type {
|
||||||
|
READ = 0;
|
||||||
|
WRITE = 1;
|
||||||
|
READWRITE = 2;
|
||||||
|
}
|
||||||
|
Type permType = 1;
|
||||||
|
|
||||||
|
bytes key = 2;
|
||||||
|
bytes range_end = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role is a single entry in the bucket authRoles
|
||||||
|
message Role {
|
||||||
|
bytes name = 1;
|
||||||
|
|
||||||
|
repeated Permission keyPermission = 2;
|
||||||
|
}
|
117
vendor/github.com/coreos/etcd/client/README.md
generated
vendored
Normal file
117
vendor/github.com/coreos/etcd/client/README.md
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
# etcd/client
|
||||||
|
|
||||||
|
etcd/client is the Go client library for etcd.
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/coreos/etcd/client)
|
||||||
|
|
||||||
|
etcd uses `cmd/vendor` directory to store external dependencies, which are
|
||||||
|
to be compiled into etcd release binaries. `client` can be imported without
|
||||||
|
vendoring. For full compatibility, it is recommended to vendor builds using
|
||||||
|
etcd's vendored packages, using tools like godep, as in
|
||||||
|
[vendor directories](https://golang.org/cmd/go/#hdr-Vendor_Directories).
|
||||||
|
For more detail, please read [Go vendor design](https://golang.org/s/go15vendor).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/coreos/etcd/client
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"github.com/coreos/etcd/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := client.Config{
|
||||||
|
Endpoints: []string{"http://127.0.0.1:2379"},
|
||||||
|
Transport: client.DefaultTransport,
|
||||||
|
// set timeout per request to fail fast when the target endpoint is unavailable
|
||||||
|
HeaderTimeoutPerRequest: time.Second,
|
||||||
|
}
|
||||||
|
c, err := client.New(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
kapi := client.NewKeysAPI(c)
|
||||||
|
// set "/foo" key with "bar" value
|
||||||
|
log.Print("Setting '/foo' key with 'bar' value")
|
||||||
|
resp, err := kapi.Set(context.Background(), "/foo", "bar", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
// print common key info
|
||||||
|
log.Printf("Set is done. Metadata is %q\n", resp)
|
||||||
|
}
|
||||||
|
// get "/foo" key's value
|
||||||
|
log.Print("Getting '/foo' key value")
|
||||||
|
resp, err = kapi.Get(context.Background(), "/foo", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
// print common key info
|
||||||
|
log.Printf("Get is done. Metadata is %q\n", resp)
|
||||||
|
// print value
|
||||||
|
log.Printf("%q key has %q value\n", resp.Node.Key, resp.Node.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
etcd client might return three types of errors.
|
||||||
|
|
||||||
|
- context error
|
||||||
|
|
||||||
|
Each API call has its first parameter as `context`. A context can be canceled or have an attached deadline. If the context is canceled or reaches its deadline, the responding context error will be returned no matter what internal errors the API call has already encountered.
|
||||||
|
|
||||||
|
- cluster error
|
||||||
|
|
||||||
|
Each API call tries to send request to the cluster endpoints one by one until it successfully gets a response. If a requests to an endpoint fails, due to exceeding per request timeout or connection issues, the error will be added into a list of errors. If all possible endpoints fail, a cluster error that includes all encountered errors will be returned.
|
||||||
|
|
||||||
|
- response error
|
||||||
|
|
||||||
|
If the response gets from the cluster is invalid, a plain string error will be returned. For example, it might be a invalid JSON error.
|
||||||
|
|
||||||
|
Here is the example code to handle client errors:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cfg := client.Config{Endpoints: []string{"http://etcd1:2379","http://etcd2:2379","http://etcd3:2379"}}
|
||||||
|
c, err := client.New(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
kapi := client.NewKeysAPI(c)
|
||||||
|
resp, err := kapi.Set(ctx, "test", "bar", nil)
|
||||||
|
if err != nil {
|
||||||
|
if err == context.Canceled {
|
||||||
|
// ctx is canceled by another routine
|
||||||
|
} else if err == context.DeadlineExceeded {
|
||||||
|
// ctx is attached with a deadline and it exceeded
|
||||||
|
} else if cerr, ok := err.(*client.ClusterError); ok {
|
||||||
|
// process (cerr.Errors)
|
||||||
|
} else {
|
||||||
|
// bad cluster endpoints, which are not etcd servers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Caveat
|
||||||
|
|
||||||
|
1. etcd/client prefers to use the same endpoint as long as the endpoint continues to work well. This saves socket resources, and improves efficiency for both client and server side. This preference doesn't remove consistency from the data consumed by the client because data replicated to each etcd member has already passed through the consensus process.
|
||||||
|
|
||||||
|
2. etcd/client does round-robin rotation on other available endpoints if the preferred endpoint isn't functioning properly. For example, if the member that etcd/client connects to is hard killed, etcd/client will fail on the first attempt with the killed member, and succeed on the second attempt with another member. If it fails to talk to all available endpoints, it will return all errors happened.
|
||||||
|
|
||||||
|
3. Default etcd/client cannot handle the case that the remote server is SIGSTOPed now. TCP keepalive mechanism doesn't help in this scenario because operating system may still send TCP keep-alive packets. Over time we'd like to improve this functionality, but solving this issue isn't high priority because a real-life case in which a server is stopped, but the connection is kept alive, hasn't been brought to our attention.
|
||||||
|
|
||||||
|
4. etcd/client cannot detect whether the member in use is healthy when doing read requests. If the member is isolated from the cluster, etcd/client may retrieve outdated data. As a workaround, users could monitor experimental /health endpoint for member healthy information. We are improving it at [#3265](https://github.com/coreos/etcd/issues/3265).
|
237
vendor/github.com/coreos/etcd/client/auth_role.go
generated
vendored
Normal file
237
vendor/github.com/coreos/etcd/client/auth_role.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
Role string `json:"role"`
|
||||||
|
Permissions Permissions `json:"permissions"`
|
||||||
|
Grant *Permissions `json:"grant,omitempty"`
|
||||||
|
Revoke *Permissions `json:"revoke,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Permissions struct {
|
||||||
|
KV rwPermission `json:"kv"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rwPermission struct {
|
||||||
|
Read []string `json:"read"`
|
||||||
|
Write []string `json:"write"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PermissionType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReadPermission PermissionType = iota
|
||||||
|
WritePermission
|
||||||
|
ReadWritePermission
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
|
||||||
|
// interact with etcd's role creation and modification features.
|
||||||
|
func NewAuthRoleAPI(c Client) AuthRoleAPI {
|
||||||
|
return &httpAuthRoleAPI{
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthRoleAPI interface {
|
||||||
|
// AddRole adds a role.
|
||||||
|
AddRole(ctx context.Context, role string) error
|
||||||
|
|
||||||
|
// RemoveRole removes a role.
|
||||||
|
RemoveRole(ctx context.Context, role string) error
|
||||||
|
|
||||||
|
// GetRole retrieves role details.
|
||||||
|
GetRole(ctx context.Context, role string) (*Role, error)
|
||||||
|
|
||||||
|
// GrantRoleKV grants a role some permission prefixes for the KV store.
|
||||||
|
GrantRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
|
||||||
|
|
||||||
|
// RevokeRoleKV revokes some permission prefixes for a role on the KV store.
|
||||||
|
RevokeRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
|
||||||
|
|
||||||
|
// ListRoles lists roles.
|
||||||
|
ListRoles(ctx context.Context) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAuthRoleAPI struct {
|
||||||
|
client httpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type authRoleAPIAction struct {
|
||||||
|
verb string
|
||||||
|
name string
|
||||||
|
role *Role
|
||||||
|
}
|
||||||
|
|
||||||
|
type authRoleAPIList struct{}
|
||||||
|
|
||||||
|
func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2AuthURL(ep, "roles", "")
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2AuthURL(ep, "roles", l.name)
|
||||||
|
if l.role == nil {
|
||||||
|
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(l.role)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
body := bytes.NewReader(b)
|
||||||
|
req, _ := http.NewRequest(l.verb, u.String(), body)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
|
||||||
|
resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var roleList struct {
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(body, &roleList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret := make([]string, 0, len(roleList.Roles))
|
||||||
|
for _, r := range roleList.Roles {
|
||||||
|
ret = append(ret, r.Role)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
|
||||||
|
role := &Role{
|
||||||
|
Role: rolename,
|
||||||
|
}
|
||||||
|
return r.addRemoveRole(ctx, &authRoleAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
name: rolename,
|
||||||
|
role: role,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
|
||||||
|
return r.addRemoveRole(ctx, &authRoleAPIAction{
|
||||||
|
verb: "DELETE",
|
||||||
|
name: rolename,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
|
||||||
|
resp, body, err := r.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err := json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sec
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
|
||||||
|
return r.modRole(ctx, &authRoleAPIAction{
|
||||||
|
verb: "GET",
|
||||||
|
name: rolename,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRWPermission(prefixes []string, permType PermissionType) rwPermission {
|
||||||
|
var out rwPermission
|
||||||
|
switch permType {
|
||||||
|
case ReadPermission:
|
||||||
|
out.Read = prefixes
|
||||||
|
case WritePermission:
|
||||||
|
out.Write = prefixes
|
||||||
|
case ReadWritePermission:
|
||||||
|
out.Read = prefixes
|
||||||
|
out.Write = prefixes
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||||
|
rwp := buildRWPermission(prefixes, permType)
|
||||||
|
role := &Role{
|
||||||
|
Role: rolename,
|
||||||
|
Grant: &Permissions{
|
||||||
|
KV: rwp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return r.modRole(ctx, &authRoleAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
name: rolename,
|
||||||
|
role: role,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
|
||||||
|
rwp := buildRWPermission(prefixes, permType)
|
||||||
|
role := &Role{
|
||||||
|
Role: rolename,
|
||||||
|
Revoke: &Permissions{
|
||||||
|
KV: rwp,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return r.modRole(ctx, &authRoleAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
name: rolename,
|
||||||
|
role: role,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
|
||||||
|
resp, body, err := r.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err = json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, sec
|
||||||
|
}
|
||||||
|
var role Role
|
||||||
|
if err = json.Unmarshal(body, &role); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &role, nil
|
||||||
|
}
|
320
vendor/github.com/coreos/etcd/client/auth_user.go
generated
vendored
Normal file
320
vendor/github.com/coreos/etcd/client/auth_user.go
generated
vendored
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultV2AuthPrefix = "/v2/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
|
Grant []string `json:"grant,omitempty"`
|
||||||
|
Revoke []string `json:"revoke,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// userListEntry is the user representation given by the server for ListUsers
|
||||||
|
type userListEntry struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRoles struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func v2AuthURL(ep url.URL, action string, name string) *url.URL {
|
||||||
|
if name != "" {
|
||||||
|
ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
|
||||||
|
return &ep
|
||||||
|
}
|
||||||
|
ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
|
||||||
|
return &ep
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthAPI constructs a new AuthAPI that uses HTTP to
|
||||||
|
// interact with etcd's general auth features.
|
||||||
|
func NewAuthAPI(c Client) AuthAPI {
|
||||||
|
return &httpAuthAPI{
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthAPI interface {
|
||||||
|
// Enable auth.
|
||||||
|
Enable(ctx context.Context) error
|
||||||
|
|
||||||
|
// Disable auth.
|
||||||
|
Disable(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAuthAPI struct {
|
||||||
|
client httpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *httpAuthAPI) Enable(ctx context.Context) error {
|
||||||
|
return s.enableDisable(ctx, &authAPIAction{"PUT"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *httpAuthAPI) Disable(ctx context.Context) error {
|
||||||
|
return s.enableDisable(ctx, &authAPIAction{"DELETE"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
|
||||||
|
resp, body, err := s.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err = json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sec
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type authAPIAction struct {
|
||||||
|
verb string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2AuthURL(ep, "enable", "")
|
||||||
|
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type authError struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code int `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e authError) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
|
||||||
|
// interact with etcd's user creation and modification features.
|
||||||
|
func NewAuthUserAPI(c Client) AuthUserAPI {
|
||||||
|
return &httpAuthUserAPI{
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthUserAPI interface {
|
||||||
|
// AddUser adds a user.
|
||||||
|
AddUser(ctx context.Context, username string, password string) error
|
||||||
|
|
||||||
|
// RemoveUser removes a user.
|
||||||
|
RemoveUser(ctx context.Context, username string) error
|
||||||
|
|
||||||
|
// GetUser retrieves user details.
|
||||||
|
GetUser(ctx context.Context, username string) (*User, error)
|
||||||
|
|
||||||
|
// GrantUser grants a user some permission roles.
|
||||||
|
GrantUser(ctx context.Context, username string, roles []string) (*User, error)
|
||||||
|
|
||||||
|
// RevokeUser revokes some permission roles from a user.
|
||||||
|
RevokeUser(ctx context.Context, username string, roles []string) (*User, error)
|
||||||
|
|
||||||
|
// ChangePassword changes the user's password.
|
||||||
|
ChangePassword(ctx context.Context, username string, password string) (*User, error)
|
||||||
|
|
||||||
|
// ListUsers lists the users.
|
||||||
|
ListUsers(ctx context.Context) ([]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpAuthUserAPI struct {
|
||||||
|
client httpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
type authUserAPIAction struct {
|
||||||
|
verb string
|
||||||
|
username string
|
||||||
|
user *User
|
||||||
|
}
|
||||||
|
|
||||||
|
type authUserAPIList struct{}
|
||||||
|
|
||||||
|
func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2AuthURL(ep, "users", "")
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2AuthURL(ep, "users", l.username)
|
||||||
|
if l.user == nil {
|
||||||
|
req, _ := http.NewRequest(l.verb, u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(l.user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
body := bytes.NewReader(b)
|
||||||
|
req, _ := http.NewRequest(l.verb, u.String(), body)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
|
||||||
|
resp, body, err := u.client.Do(ctx, &authUserAPIList{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err = json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, sec
|
||||||
|
}
|
||||||
|
|
||||||
|
var userList struct {
|
||||||
|
Users []userListEntry `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.Unmarshal(body, &userList); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]string, 0, len(userList.Users))
|
||||||
|
for _, u := range userList.Users {
|
||||||
|
ret = append(ret, u.User)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
|
||||||
|
user := &User{
|
||||||
|
User: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
return u.addRemoveUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
username: username,
|
||||||
|
user: user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
|
||||||
|
return u.addRemoveUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "DELETE",
|
||||||
|
username: username,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
|
||||||
|
resp, body, err := u.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err = json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sec
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
|
||||||
|
return u.modUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "GET",
|
||||||
|
username: username,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||||
|
user := &User{
|
||||||
|
User: username,
|
||||||
|
Grant: roles,
|
||||||
|
}
|
||||||
|
return u.modUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
username: username,
|
||||||
|
user: user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
|
||||||
|
user := &User{
|
||||||
|
User: username,
|
||||||
|
Revoke: roles,
|
||||||
|
}
|
||||||
|
return u.modUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
username: username,
|
||||||
|
user: user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
|
||||||
|
user := &User{
|
||||||
|
User: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
return u.modUser(ctx, &authUserAPIAction{
|
||||||
|
verb: "PUT",
|
||||||
|
username: username,
|
||||||
|
user: user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
|
||||||
|
resp, body, err := u.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
var sec authError
|
||||||
|
err = json.Unmarshal(body, &sec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, sec
|
||||||
|
}
|
||||||
|
var user User
|
||||||
|
if err = json.Unmarshal(body, &user); err != nil {
|
||||||
|
var userR UserRoles
|
||||||
|
if urerr := json.Unmarshal(body, &userR); urerr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
user.User = userR.User
|
||||||
|
for _, r := range userR.Roles {
|
||||||
|
user.Roles = append(user.Roles, r.Role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
|
@ -2,14 +2,13 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build go1.5
|
// borrowed from golang/net/context/ctxhttp/cancelreq.go
|
||||||
|
|
||||||
package ctxhttp
|
package client
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
func canceler(client *http.Client, req *http.Request) func() {
|
func requestCanceler(tr CancelableTransport, req *http.Request) func() {
|
||||||
// TODO(djd): Respect any existing value of req.Cancel.
|
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
req.Cancel = ch
|
req.Cancel = ch
|
||||||
|
|
609
vendor/github.com/coreos/etcd/client/client.go
generated
vendored
Normal file
609
vendor/github.com/coreos/etcd/client/client.go
generated
vendored
Normal file
|
@ -0,0 +1,609 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoEndpoints = errors.New("client: no endpoints available")
|
||||||
|
ErrTooManyRedirects = errors.New("client: too many redirects")
|
||||||
|
ErrClusterUnavailable = errors.New("client: etcd cluster is unavailable or misconfigured")
|
||||||
|
ErrNoLeaderEndpoint = errors.New("client: no leader endpoint available")
|
||||||
|
errTooManyRedirectChecks = errors.New("client: too many redirect checks")
|
||||||
|
|
||||||
|
// oneShotCtxValue is set on a context using WithValue(&oneShotValue) so
|
||||||
|
// that Do() will not retry a request
|
||||||
|
oneShotCtxValue interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultRequestTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
var DefaultTransport CancelableTransport = &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).Dial,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndpointSelectionMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// EndpointSelectionRandom is the default value of the 'SelectionMode'.
|
||||||
|
// As the name implies, the client object will pick a node from the members
|
||||||
|
// of the cluster in a random fashion. If the cluster has three members, A, B,
|
||||||
|
// and C, the client picks any node from its three members as its request
|
||||||
|
// destination.
|
||||||
|
EndpointSelectionRandom EndpointSelectionMode = iota
|
||||||
|
|
||||||
|
// If 'SelectionMode' is set to 'EndpointSelectionPrioritizeLeader',
|
||||||
|
// requests are sent directly to the cluster leader. This reduces
|
||||||
|
// forwarding roundtrips compared to making requests to etcd followers
|
||||||
|
// who then forward them to the cluster leader. In the event of a leader
|
||||||
|
// failure, however, clients configured this way cannot prioritize among
|
||||||
|
// the remaining etcd followers. Therefore, when a client sets 'SelectionMode'
|
||||||
|
// to 'EndpointSelectionPrioritizeLeader', it must use 'client.AutoSync()' to
|
||||||
|
// maintain its knowledge of current cluster state.
|
||||||
|
//
|
||||||
|
// This mode should be used with Client.AutoSync().
|
||||||
|
EndpointSelectionPrioritizeLeader
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// Endpoints defines a set of URLs (schemes, hosts and ports only)
|
||||||
|
// that can be used to communicate with a logical etcd cluster. For
|
||||||
|
// example, a three-node cluster could be provided like so:
|
||||||
|
//
|
||||||
|
// Endpoints: []string{
|
||||||
|
// "http://node1.example.com:2379",
|
||||||
|
// "http://node2.example.com:2379",
|
||||||
|
// "http://node3.example.com:2379",
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// If multiple endpoints are provided, the Client will attempt to
|
||||||
|
// use them all in the event that one or more of them are unusable.
|
||||||
|
//
|
||||||
|
// If Client.Sync is ever called, the Client may cache an alternate
|
||||||
|
// set of endpoints to continue operation.
|
||||||
|
Endpoints []string
|
||||||
|
|
||||||
|
// Transport is used by the Client to drive HTTP requests. If not
|
||||||
|
// provided, DefaultTransport will be used.
|
||||||
|
Transport CancelableTransport
|
||||||
|
|
||||||
|
// CheckRedirect specifies the policy for handling HTTP redirects.
|
||||||
|
// If CheckRedirect is not nil, the Client calls it before
|
||||||
|
// following an HTTP redirect. The sole argument is the number of
|
||||||
|
// requests that have already been made. If CheckRedirect returns
|
||||||
|
// an error, Client.Do will not make any further requests and return
|
||||||
|
// the error back it to the caller.
|
||||||
|
//
|
||||||
|
// If CheckRedirect is nil, the Client uses its default policy,
|
||||||
|
// which is to stop after 10 consecutive requests.
|
||||||
|
CheckRedirect CheckRedirectFunc
|
||||||
|
|
||||||
|
// Username specifies the user credential to add as an authorization header
|
||||||
|
Username string
|
||||||
|
|
||||||
|
// Password is the password for the specified user to add as an authorization header
|
||||||
|
// to the request.
|
||||||
|
Password string
|
||||||
|
|
||||||
|
// HeaderTimeoutPerRequest specifies the time limit to wait for response
|
||||||
|
// header in a single request made by the Client. The timeout includes
|
||||||
|
// connection time, any redirects, and header wait time.
|
||||||
|
//
|
||||||
|
// For non-watch GET request, server returns the response body immediately.
|
||||||
|
// For PUT/POST/DELETE request, server will attempt to commit request
|
||||||
|
// before responding, which is expected to take `100ms + 2 * RTT`.
|
||||||
|
// For watch request, server returns the header immediately to notify Client
|
||||||
|
// watch start. But if server is behind some kind of proxy, the response
|
||||||
|
// header may be cached at proxy, and Client cannot rely on this behavior.
|
||||||
|
//
|
||||||
|
// Especially, wait request will ignore this timeout.
|
||||||
|
//
|
||||||
|
// One API call may send multiple requests to different etcd servers until it
|
||||||
|
// succeeds. Use context of the API to specify the overall timeout.
|
||||||
|
//
|
||||||
|
// A HeaderTimeoutPerRequest of zero means no timeout.
|
||||||
|
HeaderTimeoutPerRequest time.Duration
|
||||||
|
|
||||||
|
// SelectionMode is an EndpointSelectionMode enum that specifies the
|
||||||
|
// policy for choosing the etcd cluster node to which requests are sent.
|
||||||
|
SelectionMode EndpointSelectionMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) transport() CancelableTransport {
|
||||||
|
if cfg.Transport == nil {
|
||||||
|
return DefaultTransport
|
||||||
|
}
|
||||||
|
return cfg.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) checkRedirect() CheckRedirectFunc {
|
||||||
|
if cfg.CheckRedirect == nil {
|
||||||
|
return DefaultCheckRedirect
|
||||||
|
}
|
||||||
|
return cfg.CheckRedirect
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelableTransport mimics net/http.Transport, but requires that
|
||||||
|
// the object also support request cancellation.
|
||||||
|
type CancelableTransport interface {
|
||||||
|
http.RoundTripper
|
||||||
|
CancelRequest(req *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckRedirectFunc func(via int) error
|
||||||
|
|
||||||
|
// DefaultCheckRedirect follows up to 10 redirects, but no more.
|
||||||
|
var DefaultCheckRedirect CheckRedirectFunc = func(via int) error {
|
||||||
|
if via > 10 {
|
||||||
|
return ErrTooManyRedirects
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client interface {
|
||||||
|
// Sync updates the internal cache of the etcd cluster's membership.
|
||||||
|
Sync(context.Context) error
|
||||||
|
|
||||||
|
// AutoSync periodically calls Sync() every given interval.
|
||||||
|
// The recommended sync interval is 10 seconds to 1 minute, which does
|
||||||
|
// not bring too much overhead to server and makes client catch up the
|
||||||
|
// cluster change in time.
|
||||||
|
//
|
||||||
|
// The example to use it:
|
||||||
|
//
|
||||||
|
// for {
|
||||||
|
// err := client.AutoSync(ctx, 10*time.Second)
|
||||||
|
// if err == context.DeadlineExceeded || err == context.Canceled {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// log.Print(err)
|
||||||
|
// }
|
||||||
|
AutoSync(context.Context, time.Duration) error
|
||||||
|
|
||||||
|
// Endpoints returns a copy of the current set of API endpoints used
|
||||||
|
// by Client to resolve HTTP requests. If Sync has ever been called,
|
||||||
|
// this may differ from the initial Endpoints provided in the Config.
|
||||||
|
Endpoints() []string
|
||||||
|
|
||||||
|
// SetEndpoints sets the set of API endpoints used by Client to resolve
|
||||||
|
// HTTP requests. If the given endpoints are not valid, an error will be
|
||||||
|
// returned
|
||||||
|
SetEndpoints(eps []string) error
|
||||||
|
|
||||||
|
httpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg Config) (Client, error) {
|
||||||
|
c := &httpClusterClient{
|
||||||
|
clientFactory: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect(), cfg.HeaderTimeoutPerRequest),
|
||||||
|
rand: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))),
|
||||||
|
selectionMode: cfg.SelectionMode,
|
||||||
|
}
|
||||||
|
if cfg.Username != "" {
|
||||||
|
c.credentials = &credentials{
|
||||||
|
username: cfg.Username,
|
||||||
|
password: cfg.Password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := c.SetEndpoints(cfg.Endpoints); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpClient interface {
|
||||||
|
Do(context.Context, httpAction) (*http.Response, []byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHTTPClientFactory(tr CancelableTransport, cr CheckRedirectFunc, headerTimeout time.Duration) httpClientFactory {
|
||||||
|
return func(ep url.URL) httpClient {
|
||||||
|
return &redirectFollowingHTTPClient{
|
||||||
|
checkRedirect: cr,
|
||||||
|
client: &simpleHTTPClient{
|
||||||
|
transport: tr,
|
||||||
|
endpoint: ep,
|
||||||
|
headerTimeout: headerTimeout,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type credentials struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpClientFactory func(url.URL) httpClient
|
||||||
|
|
||||||
|
type httpAction interface {
|
||||||
|
HTTPRequest(url.URL) *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpClusterClient struct {
|
||||||
|
clientFactory httpClientFactory
|
||||||
|
endpoints []url.URL
|
||||||
|
pinned int
|
||||||
|
credentials *credentials
|
||||||
|
sync.RWMutex
|
||||||
|
rand *rand.Rand
|
||||||
|
selectionMode EndpointSelectionMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) getLeaderEndpoint() (string, error) {
|
||||||
|
mAPI := NewMembersAPI(c)
|
||||||
|
leader, err := mAPI.Leader(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return leader.ClientURLs[0], nil // TODO: how to handle multiple client URLs?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) SetEndpoints(eps []string) error {
|
||||||
|
if len(eps) == 0 {
|
||||||
|
return ErrNoEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
neps := make([]url.URL, len(eps))
|
||||||
|
for i, ep := range eps {
|
||||||
|
u, err := url.Parse(ep)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
neps[i] = *u
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.selectionMode {
|
||||||
|
case EndpointSelectionRandom:
|
||||||
|
c.endpoints = shuffleEndpoints(c.rand, neps)
|
||||||
|
c.pinned = 0
|
||||||
|
case EndpointSelectionPrioritizeLeader:
|
||||||
|
c.endpoints = neps
|
||||||
|
lep, err := c.getLeaderEndpoint()
|
||||||
|
if err != nil {
|
||||||
|
return ErrNoLeaderEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range c.endpoints {
|
||||||
|
if c.endpoints[i].String() == lep {
|
||||||
|
c.pinned = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If endpoints doesn't have the lu, just keep c.pinned = 0.
|
||||||
|
// Forwarding between follower and leader would be required but it works.
|
||||||
|
default:
|
||||||
|
return errors.New(fmt.Sprintf("invalid endpoint selection mode: %d", c.selectionMode))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
|
||||||
|
action := act
|
||||||
|
c.RLock()
|
||||||
|
leps := len(c.endpoints)
|
||||||
|
eps := make([]url.URL, leps)
|
||||||
|
n := copy(eps, c.endpoints)
|
||||||
|
pinned := c.pinned
|
||||||
|
|
||||||
|
if c.credentials != nil {
|
||||||
|
action = &authedAction{
|
||||||
|
act: act,
|
||||||
|
credentials: *c.credentials,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.RUnlock()
|
||||||
|
|
||||||
|
if leps == 0 {
|
||||||
|
return nil, nil, ErrNoEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
if leps != n {
|
||||||
|
return nil, nil, errors.New("unable to pick endpoint: copy failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var body []byte
|
||||||
|
var err error
|
||||||
|
cerr := &ClusterError{}
|
||||||
|
isOneShot := ctx.Value(&oneShotCtxValue) != nil
|
||||||
|
|
||||||
|
for i := pinned; i < leps+pinned; i++ {
|
||||||
|
k := i % leps
|
||||||
|
hc := c.clientFactory(eps[k])
|
||||||
|
resp, body, err = hc.Do(ctx, action)
|
||||||
|
if err != nil {
|
||||||
|
cerr.Errors = append(cerr.Errors, err)
|
||||||
|
if err == ctx.Err() {
|
||||||
|
return nil, nil, ctx.Err()
|
||||||
|
}
|
||||||
|
if err == context.Canceled || err == context.DeadlineExceeded {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if isOneShot {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if resp.StatusCode/100 == 5 {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusInternalServerError, http.StatusServiceUnavailable:
|
||||||
|
// TODO: make sure this is a no leader response
|
||||||
|
cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s has no leader", eps[k].String()))
|
||||||
|
default:
|
||||||
|
cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s returns server error [%s]", eps[k].String(), http.StatusText(resp.StatusCode)))
|
||||||
|
}
|
||||||
|
if isOneShot {
|
||||||
|
return nil, nil, cerr.Errors[0]
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if k != pinned {
|
||||||
|
c.Lock()
|
||||||
|
c.pinned = k
|
||||||
|
c.Unlock()
|
||||||
|
}
|
||||||
|
return resp, body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, cerr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) Endpoints() []string {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
eps := make([]string, len(c.endpoints))
|
||||||
|
for i, ep := range c.endpoints {
|
||||||
|
eps[i] = ep.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return eps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) Sync(ctx context.Context) error {
|
||||||
|
mAPI := NewMembersAPI(c)
|
||||||
|
ms, err := mAPI.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
eps := make([]string, 0)
|
||||||
|
for _, m := range ms {
|
||||||
|
eps = append(eps, m.ClientURLs...)
|
||||||
|
}
|
||||||
|
sort.Sort(sort.StringSlice(eps))
|
||||||
|
|
||||||
|
ceps := make([]string, len(c.endpoints))
|
||||||
|
for i, cep := range c.endpoints {
|
||||||
|
ceps[i] = cep.String()
|
||||||
|
}
|
||||||
|
sort.Sort(sort.StringSlice(ceps))
|
||||||
|
// fast path if no change happens
|
||||||
|
// this helps client to pin the endpoint when no cluster change
|
||||||
|
if reflect.DeepEqual(eps, ceps) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SetEndpoints(eps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *httpClusterClient) AutoSync(ctx context.Context, interval time.Duration) error {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
err := c.Sync(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type roundTripResponse struct {
|
||||||
|
resp *http.Response
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleHTTPClient struct {
|
||||||
|
transport CancelableTransport
|
||||||
|
endpoint url.URL
|
||||||
|
headerTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
|
||||||
|
req := act.HTTPRequest(c.endpoint)
|
||||||
|
|
||||||
|
if err := printcURL(req); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isWait := false
|
||||||
|
if req != nil && req.URL != nil {
|
||||||
|
ws := req.URL.Query().Get("wait")
|
||||||
|
if len(ws) != 0 {
|
||||||
|
var err error
|
||||||
|
isWait, err = strconv.ParseBool(ws)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("wrong wait value %s (%v for %+v)", ws, err, req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hctx context.Context
|
||||||
|
var hcancel context.CancelFunc
|
||||||
|
if !isWait && c.headerTimeout > 0 {
|
||||||
|
hctx, hcancel = context.WithTimeout(ctx, c.headerTimeout)
|
||||||
|
} else {
|
||||||
|
hctx, hcancel = context.WithCancel(ctx)
|
||||||
|
}
|
||||||
|
defer hcancel()
|
||||||
|
|
||||||
|
reqcancel := requestCanceler(c.transport, req)
|
||||||
|
|
||||||
|
rtchan := make(chan roundTripResponse, 1)
|
||||||
|
go func() {
|
||||||
|
resp, err := c.transport.RoundTrip(req)
|
||||||
|
rtchan <- roundTripResponse{resp: resp, err: err}
|
||||||
|
close(rtchan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
select {
|
||||||
|
case rtresp := <-rtchan:
|
||||||
|
resp, err = rtresp.resp, rtresp.err
|
||||||
|
case <-hctx.Done():
|
||||||
|
// cancel and wait for request to actually exit before continuing
|
||||||
|
reqcancel()
|
||||||
|
rtresp := <-rtchan
|
||||||
|
resp = rtresp.resp
|
||||||
|
switch {
|
||||||
|
case ctx.Err() != nil:
|
||||||
|
err = ctx.Err()
|
||||||
|
case hctx.Err() != nil:
|
||||||
|
err = fmt.Errorf("client: endpoint %s exceeded header timeout", c.endpoint.String())
|
||||||
|
default:
|
||||||
|
panic("failed to get error from context")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always check for resp nil-ness to deal with possible
|
||||||
|
// race conditions between channels above
|
||||||
|
defer func() {
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var body []byte
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
resp.Body.Close()
|
||||||
|
<-done
|
||||||
|
return nil, nil, ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type authedAction struct {
|
||||||
|
act httpAction
|
||||||
|
credentials credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authedAction) HTTPRequest(url url.URL) *http.Request {
|
||||||
|
r := a.act.HTTPRequest(url)
|
||||||
|
r.SetBasicAuth(a.credentials.username, a.credentials.password)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type redirectFollowingHTTPClient struct {
|
||||||
|
client httpClient
|
||||||
|
checkRedirect CheckRedirectFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *redirectFollowingHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
|
||||||
|
next := act
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
if err := r.checkRedirect(i); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp, body, err := r.client.Do(ctx, next)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode/100 == 3 {
|
||||||
|
hdr := resp.Header.Get("Location")
|
||||||
|
if hdr == "" {
|
||||||
|
return nil, nil, fmt.Errorf("Location header not set")
|
||||||
|
}
|
||||||
|
loc, err := url.Parse(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("Location header not valid URL: %s", hdr)
|
||||||
|
}
|
||||||
|
next = &redirectedHTTPAction{
|
||||||
|
action: act,
|
||||||
|
location: *loc,
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return resp, body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, errTooManyRedirectChecks
|
||||||
|
}
|
||||||
|
|
||||||
|
type redirectedHTTPAction struct {
|
||||||
|
action httpAction
|
||||||
|
location url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
orig := r.action.HTTPRequest(ep)
|
||||||
|
orig.URL = &r.location
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func shuffleEndpoints(r *rand.Rand, eps []url.URL) []url.URL {
|
||||||
|
p := r.Perm(len(eps))
|
||||||
|
neps := make([]url.URL, len(eps))
|
||||||
|
for i, k := range p {
|
||||||
|
neps[i] = eps[k]
|
||||||
|
}
|
||||||
|
return neps
|
||||||
|
}
|
33
vendor/github.com/coreos/etcd/client/cluster_error.go
generated
vendored
Normal file
33
vendor/github.com/coreos/etcd/client/cluster_error.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ClusterError struct {
|
||||||
|
Errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce *ClusterError) Error() string {
|
||||||
|
return ErrClusterUnavailable.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce *ClusterError) Detail() string {
|
||||||
|
s := ""
|
||||||
|
for i, e := range ce.Errors {
|
||||||
|
s += fmt.Sprintf("error #%d: %s\n", i, e)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
70
vendor/github.com/coreos/etcd/client/curl.go
generated
vendored
Normal file
70
vendor/github.com/coreos/etcd/client/curl.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cURLDebug = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func EnablecURLDebug() {
|
||||||
|
cURLDebug = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisablecURLDebug() {
|
||||||
|
cURLDebug = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// printcURL prints the cURL equivalent request to stderr.
|
||||||
|
// It returns an error if the body of the request cannot
|
||||||
|
// be read.
|
||||||
|
// The caller MUST cancel the request if there is an error.
|
||||||
|
func printcURL(req *http.Request) error {
|
||||||
|
if !cURLDebug {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
command string
|
||||||
|
b []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if req.URL != nil {
|
||||||
|
command = fmt.Sprintf("curl -X %s %s", req.Method, req.URL.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Body != nil {
|
||||||
|
b, err = ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
command += fmt.Sprintf(" -d %q", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "cURL Command: %s\n", command)
|
||||||
|
|
||||||
|
// reset body
|
||||||
|
body := bytes.NewBuffer(b)
|
||||||
|
req.Body = ioutil.NopCloser(body)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
21
vendor/github.com/coreos/etcd/client/discover.go
generated
vendored
Normal file
21
vendor/github.com/coreos/etcd/client/discover.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
// Discoverer is an interface that wraps the Discover method.
|
||||||
|
type Discoverer interface {
|
||||||
|
// Discover looks up the etcd servers for the domain.
|
||||||
|
Discover(domain string) ([]string, error)
|
||||||
|
}
|
73
vendor/github.com/coreos/etcd/client/doc.go
generated
vendored
Normal file
73
vendor/github.com/coreos/etcd/client/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// 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 client provides bindings for the etcd APIs.
|
||||||
|
|
||||||
|
Create a Config and exchange it for a Client:
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/client"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
cfg := client.Config{
|
||||||
|
Endpoints: []string{"http://127.0.0.1:2379"},
|
||||||
|
Transport: DefaultTransport,
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := client.New(cfg)
|
||||||
|
if err != nil {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
Clients are safe for concurrent use by multiple goroutines.
|
||||||
|
|
||||||
|
Create a KeysAPI using the Client, then use it to interact with etcd:
|
||||||
|
|
||||||
|
kAPI := client.NewKeysAPI(c)
|
||||||
|
|
||||||
|
// create a new key /foo with the value "bar"
|
||||||
|
_, err = kAPI.Create(context.Background(), "/foo", "bar")
|
||||||
|
if err != nil {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the newly created key only if the value is still "bar"
|
||||||
|
_, err = kAPI.Delete(context.Background(), "/foo", &DeleteOptions{PrevValue: "bar"})
|
||||||
|
if err != nil {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
Use a custom context to set timeouts on your operations:
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// set a new key, ignoring it's previous state
|
||||||
|
_, err := kAPI.Set(ctx, "/ping", "pong", nil)
|
||||||
|
if err != nil {
|
||||||
|
if err == context.DeadlineExceeded {
|
||||||
|
// request took longer than 5s
|
||||||
|
} else {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package client
|
1000
vendor/github.com/coreos/etcd/client/keys.generated.go
generated
vendored
Normal file
1000
vendor/github.com/coreos/etcd/client/keys.generated.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
668
vendor/github.com/coreos/etcd/client/keys.go
generated
vendored
Normal file
668
vendor/github.com/coreos/etcd/client/keys.go
generated
vendored
Normal file
|
@ -0,0 +1,668 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
//go:generate codecgen -d 1819 -r "Node|Response|Nodes" -o keys.generated.go keys.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/pathutil"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrorCodeKeyNotFound = 100
|
||||||
|
ErrorCodeTestFailed = 101
|
||||||
|
ErrorCodeNotFile = 102
|
||||||
|
ErrorCodeNotDir = 104
|
||||||
|
ErrorCodeNodeExist = 105
|
||||||
|
ErrorCodeRootROnly = 107
|
||||||
|
ErrorCodeDirNotEmpty = 108
|
||||||
|
ErrorCodeUnauthorized = 110
|
||||||
|
|
||||||
|
ErrorCodePrevValueRequired = 201
|
||||||
|
ErrorCodeTTLNaN = 202
|
||||||
|
ErrorCodeIndexNaN = 203
|
||||||
|
ErrorCodeInvalidField = 209
|
||||||
|
ErrorCodeInvalidForm = 210
|
||||||
|
|
||||||
|
ErrorCodeRaftInternal = 300
|
||||||
|
ErrorCodeLeaderElect = 301
|
||||||
|
|
||||||
|
ErrorCodeWatcherCleared = 400
|
||||||
|
ErrorCodeEventIndexCleared = 401
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Code int `json:"errorCode"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Cause string `json:"cause"`
|
||||||
|
Index uint64 `json:"index"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return fmt.Sprintf("%v: %v (%v) [%v]", e.Code, e.Message, e.Cause, e.Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidJSON = errors.New("client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint.")
|
||||||
|
ErrEmptyBody = errors.New("client: response body is empty")
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrevExistType is used to define an existence condition when setting
|
||||||
|
// or deleting Nodes.
|
||||||
|
type PrevExistType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrevIgnore = PrevExistType("")
|
||||||
|
PrevExist = PrevExistType("true")
|
||||||
|
PrevNoExist = PrevExistType("false")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultV2KeysPrefix = "/v2/keys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewKeysAPI builds a KeysAPI that interacts with etcd's key-value
|
||||||
|
// API over HTTP.
|
||||||
|
func NewKeysAPI(c Client) KeysAPI {
|
||||||
|
return NewKeysAPIWithPrefix(c, defaultV2KeysPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeysAPIWithPrefix acts like NewKeysAPI, but allows the caller
|
||||||
|
// to provide a custom base URL path. This should only be used in
|
||||||
|
// very rare cases.
|
||||||
|
func NewKeysAPIWithPrefix(c Client, p string) KeysAPI {
|
||||||
|
return &httpKeysAPI{
|
||||||
|
client: c,
|
||||||
|
prefix: p,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeysAPI interface {
|
||||||
|
// Get retrieves a set of Nodes from etcd
|
||||||
|
Get(ctx context.Context, key string, opts *GetOptions) (*Response, error)
|
||||||
|
|
||||||
|
// Set assigns a new value to a Node identified by a given key. The caller
|
||||||
|
// may define a set of conditions in the SetOptions. If SetOptions.Dir=true
|
||||||
|
// then value is ignored.
|
||||||
|
Set(ctx context.Context, key, value string, opts *SetOptions) (*Response, error)
|
||||||
|
|
||||||
|
// Delete removes a Node identified by the given key, optionally destroying
|
||||||
|
// all of its children as well. The caller may define a set of required
|
||||||
|
// conditions in an DeleteOptions object.
|
||||||
|
Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error)
|
||||||
|
|
||||||
|
// Create is an alias for Set w/ PrevExist=false
|
||||||
|
Create(ctx context.Context, key, value string) (*Response, error)
|
||||||
|
|
||||||
|
// CreateInOrder is used to atomically create in-order keys within the given directory.
|
||||||
|
CreateInOrder(ctx context.Context, dir, value string, opts *CreateInOrderOptions) (*Response, error)
|
||||||
|
|
||||||
|
// Update is an alias for Set w/ PrevExist=true
|
||||||
|
Update(ctx context.Context, key, value string) (*Response, error)
|
||||||
|
|
||||||
|
// Watcher builds a new Watcher targeted at a specific Node identified
|
||||||
|
// by the given key. The Watcher may be configured at creation time
|
||||||
|
// through a WatcherOptions object. The returned Watcher is designed
|
||||||
|
// to emit events that happen to a Node, and optionally to its children.
|
||||||
|
Watcher(key string, opts *WatcherOptions) Watcher
|
||||||
|
}
|
||||||
|
|
||||||
|
type WatcherOptions struct {
|
||||||
|
// AfterIndex defines the index after-which the Watcher should
|
||||||
|
// start emitting events. For example, if a value of 5 is
|
||||||
|
// provided, the first event will have an index >= 6.
|
||||||
|
//
|
||||||
|
// Setting AfterIndex to 0 (default) means that the Watcher
|
||||||
|
// should start watching for events starting at the current
|
||||||
|
// index, whatever that may be.
|
||||||
|
AfterIndex uint64
|
||||||
|
|
||||||
|
// Recursive specifies whether or not the Watcher should emit
|
||||||
|
// events that occur in children of the given keyspace. If set
|
||||||
|
// to false (default), events will be limited to those that
|
||||||
|
// occur for the exact key.
|
||||||
|
Recursive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateInOrderOptions struct {
|
||||||
|
// TTL defines a period of time after-which the Node should
|
||||||
|
// expire and no longer exist. Values <= 0 are ignored. Given
|
||||||
|
// that the zero-value is ignored, TTL cannot be used to set
|
||||||
|
// a TTL of 0.
|
||||||
|
TTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetOptions struct {
|
||||||
|
// PrevValue specifies what the current value of the Node must
|
||||||
|
// be in order for the Set operation to succeed.
|
||||||
|
//
|
||||||
|
// Leaving this field empty means that the caller wishes to
|
||||||
|
// ignore the current value of the Node. This cannot be used
|
||||||
|
// to compare the Node's current value to an empty string.
|
||||||
|
//
|
||||||
|
// PrevValue is ignored if Dir=true
|
||||||
|
PrevValue string
|
||||||
|
|
||||||
|
// PrevIndex indicates what the current ModifiedIndex of the
|
||||||
|
// Node must be in order for the Set operation to succeed.
|
||||||
|
//
|
||||||
|
// If PrevIndex is set to 0 (default), no comparison is made.
|
||||||
|
PrevIndex uint64
|
||||||
|
|
||||||
|
// PrevExist specifies whether the Node must currently exist
|
||||||
|
// (PrevExist) or not (PrevNoExist). If the caller does not
|
||||||
|
// care about existence, set PrevExist to PrevIgnore, or simply
|
||||||
|
// leave it unset.
|
||||||
|
PrevExist PrevExistType
|
||||||
|
|
||||||
|
// TTL defines a period of time after-which the Node should
|
||||||
|
// expire and no longer exist. Values <= 0 are ignored. Given
|
||||||
|
// that the zero-value is ignored, TTL cannot be used to set
|
||||||
|
// a TTL of 0.
|
||||||
|
TTL time.Duration
|
||||||
|
|
||||||
|
// Refresh set to true means a TTL value can be updated
|
||||||
|
// without firing a watch or changing the node value. A
|
||||||
|
// value must not be provided when refreshing a key.
|
||||||
|
Refresh bool
|
||||||
|
|
||||||
|
// Dir specifies whether or not this Node should be created as a directory.
|
||||||
|
Dir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetOptions struct {
|
||||||
|
// Recursive defines whether or not all children of the Node
|
||||||
|
// should be returned.
|
||||||
|
Recursive bool
|
||||||
|
|
||||||
|
// Sort instructs the server whether or not to sort the Nodes.
|
||||||
|
// If true, the Nodes are sorted alphabetically by key in
|
||||||
|
// ascending order (A to z). If false (default), the Nodes will
|
||||||
|
// not be sorted and the ordering used should not be considered
|
||||||
|
// predictable.
|
||||||
|
Sort bool
|
||||||
|
|
||||||
|
// Quorum specifies whether it gets the latest committed value that
|
||||||
|
// has been applied in quorum of members, which ensures external
|
||||||
|
// consistency (or linearizability).
|
||||||
|
Quorum bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteOptions struct {
|
||||||
|
// PrevValue specifies what the current value of the Node must
|
||||||
|
// be in order for the Delete operation to succeed.
|
||||||
|
//
|
||||||
|
// Leaving this field empty means that the caller wishes to
|
||||||
|
// ignore the current value of the Node. This cannot be used
|
||||||
|
// to compare the Node's current value to an empty string.
|
||||||
|
PrevValue string
|
||||||
|
|
||||||
|
// PrevIndex indicates what the current ModifiedIndex of the
|
||||||
|
// Node must be in order for the Delete operation to succeed.
|
||||||
|
//
|
||||||
|
// If PrevIndex is set to 0 (default), no comparison is made.
|
||||||
|
PrevIndex uint64
|
||||||
|
|
||||||
|
// Recursive defines whether or not all children of the Node
|
||||||
|
// should be deleted. If set to true, all children of the Node
|
||||||
|
// identified by the given key will be deleted. If left unset
|
||||||
|
// or explicitly set to false, only a single Node will be
|
||||||
|
// deleted.
|
||||||
|
Recursive bool
|
||||||
|
|
||||||
|
// Dir specifies whether or not this Node should be removed as a directory.
|
||||||
|
Dir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Watcher interface {
|
||||||
|
// Next blocks until an etcd event occurs, then returns a Response
|
||||||
|
// representing that event. The behavior of Next depends on the
|
||||||
|
// WatcherOptions used to construct the Watcher. Next is designed to
|
||||||
|
// be called repeatedly, each time blocking until a subsequent event
|
||||||
|
// is available.
|
||||||
|
//
|
||||||
|
// If the provided context is cancelled, Next will return a non-nil
|
||||||
|
// error. Any other failures encountered while waiting for the next
|
||||||
|
// event (connection issues, deserialization failures, etc) will
|
||||||
|
// also result in a non-nil error.
|
||||||
|
Next(context.Context) (*Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
// Action is the name of the operation that occurred. Possible values
|
||||||
|
// include get, set, delete, update, create, compareAndSwap,
|
||||||
|
// compareAndDelete and expire.
|
||||||
|
Action string `json:"action"`
|
||||||
|
|
||||||
|
// Node represents the state of the relevant etcd Node.
|
||||||
|
Node *Node `json:"node"`
|
||||||
|
|
||||||
|
// PrevNode represents the previous state of the Node. PrevNode is non-nil
|
||||||
|
// only if the Node existed before the action occurred and the action
|
||||||
|
// caused a change to the Node.
|
||||||
|
PrevNode *Node `json:"prevNode"`
|
||||||
|
|
||||||
|
// Index holds the cluster-level index at the time the Response was generated.
|
||||||
|
// This index is not tied to the Node(s) contained in this Response.
|
||||||
|
Index uint64 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
// Key represents the unique location of this Node (e.g. "/foo/bar").
|
||||||
|
Key string `json:"key"`
|
||||||
|
|
||||||
|
// Dir reports whether node describes a directory.
|
||||||
|
Dir bool `json:"dir,omitempty"`
|
||||||
|
|
||||||
|
// Value is the current data stored on this Node. If this Node
|
||||||
|
// is a directory, Value will be empty.
|
||||||
|
Value string `json:"value"`
|
||||||
|
|
||||||
|
// Nodes holds the children of this Node, only if this Node is a directory.
|
||||||
|
// This slice of will be arbitrarily deep (children, grandchildren, great-
|
||||||
|
// grandchildren, etc.) if a recursive Get or Watch request were made.
|
||||||
|
Nodes Nodes `json:"nodes"`
|
||||||
|
|
||||||
|
// CreatedIndex is the etcd index at-which this Node was created.
|
||||||
|
CreatedIndex uint64 `json:"createdIndex"`
|
||||||
|
|
||||||
|
// ModifiedIndex is the etcd index at-which this Node was last modified.
|
||||||
|
ModifiedIndex uint64 `json:"modifiedIndex"`
|
||||||
|
|
||||||
|
// Expiration is the server side expiration time of the key.
|
||||||
|
Expiration *time.Time `json:"expiration,omitempty"`
|
||||||
|
|
||||||
|
// TTL is the time to live of the key in second.
|
||||||
|
TTL int64 `json:"ttl,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) String() string {
|
||||||
|
return fmt.Sprintf("{Key: %s, CreatedIndex: %d, ModifiedIndex: %d, TTL: %d}", n.Key, n.CreatedIndex, n.ModifiedIndex, n.TTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTLDuration returns the Node's TTL as a time.Duration object
|
||||||
|
func (n *Node) TTLDuration() time.Duration {
|
||||||
|
return time.Duration(n.TTL) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
type Nodes []*Node
|
||||||
|
|
||||||
|
// interfaces for sorting
|
||||||
|
|
||||||
|
func (ns Nodes) Len() int { return len(ns) }
|
||||||
|
func (ns Nodes) Less(i, j int) bool { return ns[i].Key < ns[j].Key }
|
||||||
|
func (ns Nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
|
||||||
|
|
||||||
|
type httpKeysAPI struct {
|
||||||
|
client httpClient
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions) (*Response, error) {
|
||||||
|
act := &setAction{
|
||||||
|
Prefix: k.prefix,
|
||||||
|
Key: key,
|
||||||
|
Value: val,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
act.PrevValue = opts.PrevValue
|
||||||
|
act.PrevIndex = opts.PrevIndex
|
||||||
|
act.PrevExist = opts.PrevExist
|
||||||
|
act.TTL = opts.TTL
|
||||||
|
act.Refresh = opts.Refresh
|
||||||
|
act.Dir = opts.Dir
|
||||||
|
}
|
||||||
|
|
||||||
|
doCtx := ctx
|
||||||
|
if act.PrevExist == PrevNoExist {
|
||||||
|
doCtx = context.WithValue(doCtx, &oneShotCtxValue, &oneShotCtxValue)
|
||||||
|
}
|
||||||
|
resp, body, err := k.client.Do(doCtx, act)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Create(ctx context.Context, key, val string) (*Response, error) {
|
||||||
|
return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevNoExist})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) CreateInOrder(ctx context.Context, dir, val string, opts *CreateInOrderOptions) (*Response, error) {
|
||||||
|
act := &createInOrderAction{
|
||||||
|
Prefix: k.prefix,
|
||||||
|
Dir: dir,
|
||||||
|
Value: val,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
act.TTL = opts.TTL
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, body, err := k.client.Do(ctx, act)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Update(ctx context.Context, key, val string) (*Response, error) {
|
||||||
|
return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevExist})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error) {
|
||||||
|
act := &deleteAction{
|
||||||
|
Prefix: k.prefix,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
act.PrevValue = opts.PrevValue
|
||||||
|
act.PrevIndex = opts.PrevIndex
|
||||||
|
act.Dir = opts.Dir
|
||||||
|
act.Recursive = opts.Recursive
|
||||||
|
}
|
||||||
|
|
||||||
|
doCtx := context.WithValue(ctx, &oneShotCtxValue, &oneShotCtxValue)
|
||||||
|
resp, body, err := k.client.Do(doCtx, act)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Get(ctx context.Context, key string, opts *GetOptions) (*Response, error) {
|
||||||
|
act := &getAction{
|
||||||
|
Prefix: k.prefix,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
act.Recursive = opts.Recursive
|
||||||
|
act.Sorted = opts.Sort
|
||||||
|
act.Quorum = opts.Quorum
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, body, err := k.client.Do(ctx, act)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *httpKeysAPI) Watcher(key string, opts *WatcherOptions) Watcher {
|
||||||
|
act := waitAction{
|
||||||
|
Prefix: k.prefix,
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
act.Recursive = opts.Recursive
|
||||||
|
if opts.AfterIndex > 0 {
|
||||||
|
act.WaitIndex = opts.AfterIndex + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpWatcher{
|
||||||
|
client: k.client,
|
||||||
|
nextWait: act,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpWatcher struct {
|
||||||
|
client httpClient
|
||||||
|
nextWait waitAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hw *httpWatcher) Next(ctx context.Context) (*Response, error) {
|
||||||
|
for {
|
||||||
|
httpresp, body, err := hw.client.Do(ctx, &hw.nextWait)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := unmarshalHTTPResponse(httpresp.StatusCode, httpresp.Header, body)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrEmptyBody {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hw.nextWait.WaitIndex = resp.Node.ModifiedIndex + 1
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2KeysURL forms a URL representing the location of a key.
|
||||||
|
// The endpoint argument represents the base URL of an etcd
|
||||||
|
// server. The prefix is the path needed to route from the
|
||||||
|
// provided endpoint's path to the root of the keys API
|
||||||
|
// (typically "/v2/keys").
|
||||||
|
func v2KeysURL(ep url.URL, prefix, key string) *url.URL {
|
||||||
|
// We concatenate all parts together manually. We cannot use
|
||||||
|
// path.Join because it does not reserve trailing slash.
|
||||||
|
// We call CanonicalURLPath to further cleanup the path.
|
||||||
|
if prefix != "" && prefix[0] != '/' {
|
||||||
|
prefix = "/" + prefix
|
||||||
|
}
|
||||||
|
if key != "" && key[0] != '/' {
|
||||||
|
key = "/" + key
|
||||||
|
}
|
||||||
|
ep.Path = pathutil.CanonicalURLPath(ep.Path + prefix + key)
|
||||||
|
return &ep
|
||||||
|
}
|
||||||
|
|
||||||
|
type getAction struct {
|
||||||
|
Prefix string
|
||||||
|
Key string
|
||||||
|
Recursive bool
|
||||||
|
Sorted bool
|
||||||
|
Quorum bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *getAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2KeysURL(ep, g.Prefix, g.Key)
|
||||||
|
|
||||||
|
params := u.Query()
|
||||||
|
params.Set("recursive", strconv.FormatBool(g.Recursive))
|
||||||
|
params.Set("sorted", strconv.FormatBool(g.Sorted))
|
||||||
|
params.Set("quorum", strconv.FormatBool(g.Quorum))
|
||||||
|
u.RawQuery = params.Encode()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type waitAction struct {
|
||||||
|
Prefix string
|
||||||
|
Key string
|
||||||
|
WaitIndex uint64
|
||||||
|
Recursive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *waitAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2KeysURL(ep, w.Prefix, w.Key)
|
||||||
|
|
||||||
|
params := u.Query()
|
||||||
|
params.Set("wait", "true")
|
||||||
|
params.Set("waitIndex", strconv.FormatUint(w.WaitIndex, 10))
|
||||||
|
params.Set("recursive", strconv.FormatBool(w.Recursive))
|
||||||
|
u.RawQuery = params.Encode()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type setAction struct {
|
||||||
|
Prefix string
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
PrevValue string
|
||||||
|
PrevIndex uint64
|
||||||
|
PrevExist PrevExistType
|
||||||
|
TTL time.Duration
|
||||||
|
Refresh bool
|
||||||
|
Dir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *setAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2KeysURL(ep, a.Prefix, a.Key)
|
||||||
|
|
||||||
|
params := u.Query()
|
||||||
|
form := url.Values{}
|
||||||
|
|
||||||
|
// we're either creating a directory or setting a key
|
||||||
|
if a.Dir {
|
||||||
|
params.Set("dir", strconv.FormatBool(a.Dir))
|
||||||
|
} else {
|
||||||
|
// These options are only valid for setting a key
|
||||||
|
if a.PrevValue != "" {
|
||||||
|
params.Set("prevValue", a.PrevValue)
|
||||||
|
}
|
||||||
|
form.Add("value", a.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options which apply to both setting a key and creating a dir
|
||||||
|
if a.PrevIndex != 0 {
|
||||||
|
params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
|
||||||
|
}
|
||||||
|
if a.PrevExist != PrevIgnore {
|
||||||
|
params.Set("prevExist", string(a.PrevExist))
|
||||||
|
}
|
||||||
|
if a.TTL > 0 {
|
||||||
|
form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Refresh {
|
||||||
|
form.Add("refresh", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
u.RawQuery = params.Encode()
|
||||||
|
body := strings.NewReader(form.Encode())
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", u.String(), body)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type deleteAction struct {
|
||||||
|
Prefix string
|
||||||
|
Key string
|
||||||
|
PrevValue string
|
||||||
|
PrevIndex uint64
|
||||||
|
Dir bool
|
||||||
|
Recursive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *deleteAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2KeysURL(ep, a.Prefix, a.Key)
|
||||||
|
|
||||||
|
params := u.Query()
|
||||||
|
if a.PrevValue != "" {
|
||||||
|
params.Set("prevValue", a.PrevValue)
|
||||||
|
}
|
||||||
|
if a.PrevIndex != 0 {
|
||||||
|
params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
|
||||||
|
}
|
||||||
|
if a.Dir {
|
||||||
|
params.Set("dir", "true")
|
||||||
|
}
|
||||||
|
if a.Recursive {
|
||||||
|
params.Set("recursive", "true")
|
||||||
|
}
|
||||||
|
u.RawQuery = params.Encode()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("DELETE", u.String(), nil)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type createInOrderAction struct {
|
||||||
|
Prefix string
|
||||||
|
Dir string
|
||||||
|
Value string
|
||||||
|
TTL time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *createInOrderAction) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2KeysURL(ep, a.Prefix, a.Dir)
|
||||||
|
|
||||||
|
form := url.Values{}
|
||||||
|
form.Add("value", a.Value)
|
||||||
|
if a.TTL > 0 {
|
||||||
|
form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
|
||||||
|
}
|
||||||
|
body := strings.NewReader(form.Encode())
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("POST", u.String(), body)
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Response, err error) {
|
||||||
|
switch code {
|
||||||
|
case http.StatusOK, http.StatusCreated:
|
||||||
|
if len(body) == 0 {
|
||||||
|
return nil, ErrEmptyBody
|
||||||
|
}
|
||||||
|
res, err = unmarshalSuccessfulKeysResponse(header, body)
|
||||||
|
default:
|
||||||
|
err = unmarshalFailedKeysResponse(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) {
|
||||||
|
var res Response
|
||||||
|
err := codec.NewDecoderBytes(body, new(codec.JsonHandle)).Decode(&res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidJSON
|
||||||
|
}
|
||||||
|
if header.Get("X-Etcd-Index") != "" {
|
||||||
|
res.Index, err = strconv.ParseUint(header.Get("X-Etcd-Index"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalFailedKeysResponse(body []byte) error {
|
||||||
|
var etcdErr Error
|
||||||
|
if err := json.Unmarshal(body, &etcdErr); err != nil {
|
||||||
|
return ErrInvalidJSON
|
||||||
|
}
|
||||||
|
return etcdErr
|
||||||
|
}
|
304
vendor/github.com/coreos/etcd/client/members.go
generated
vendored
Normal file
304
vendor/github.com/coreos/etcd/client/members.go
generated
vendored
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultV2MembersPrefix = "/v2/members"
|
||||||
|
defaultLeaderSuffix = "/leader"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
// ID is the unique identifier of this Member.
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// Name is a human-readable, non-unique identifier of this Member.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// PeerURLs represents the HTTP(S) endpoints this Member uses to
|
||||||
|
// participate in etcd's consensus protocol.
|
||||||
|
PeerURLs []string `json:"peerURLs"`
|
||||||
|
|
||||||
|
// ClientURLs represents the HTTP(S) endpoints on which this Member
|
||||||
|
// serves it's client-facing APIs.
|
||||||
|
ClientURLs []string `json:"clientURLs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type memberCollection []Member
|
||||||
|
|
||||||
|
func (c *memberCollection) UnmarshalJSON(data []byte) error {
|
||||||
|
d := struct {
|
||||||
|
Members []Member
|
||||||
|
}{}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &d); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.Members == nil {
|
||||||
|
*c = make([]Member, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*c = d.Members
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type memberCreateOrUpdateRequest struct {
|
||||||
|
PeerURLs types.URLs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *memberCreateOrUpdateRequest) MarshalJSON() ([]byte, error) {
|
||||||
|
s := struct {
|
||||||
|
PeerURLs []string `json:"peerURLs"`
|
||||||
|
}{
|
||||||
|
PeerURLs: make([]string, len(m.PeerURLs)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, u := range m.PeerURLs {
|
||||||
|
s.PeerURLs[i] = u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(&s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMembersAPI constructs a new MembersAPI that uses HTTP to
|
||||||
|
// interact with etcd's membership API.
|
||||||
|
func NewMembersAPI(c Client) MembersAPI {
|
||||||
|
return &httpMembersAPI{
|
||||||
|
client: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MembersAPI interface {
|
||||||
|
// List enumerates the current cluster membership.
|
||||||
|
List(ctx context.Context) ([]Member, error)
|
||||||
|
|
||||||
|
// Add instructs etcd to accept a new Member into the cluster.
|
||||||
|
Add(ctx context.Context, peerURL string) (*Member, error)
|
||||||
|
|
||||||
|
// Remove demotes an existing Member out of the cluster.
|
||||||
|
Remove(ctx context.Context, mID string) error
|
||||||
|
|
||||||
|
// Update instructs etcd to update an existing Member in the cluster.
|
||||||
|
Update(ctx context.Context, mID string, peerURLs []string) error
|
||||||
|
|
||||||
|
// Leader gets current leader of the cluster
|
||||||
|
Leader(ctx context.Context) (*Member, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpMembersAPI struct {
|
||||||
|
client httpClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *httpMembersAPI) List(ctx context.Context) ([]Member, error) {
|
||||||
|
req := &membersAPIActionList{}
|
||||||
|
resp, body, err := m.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mCollection memberCollection
|
||||||
|
if err := json.Unmarshal(body, &mCollection); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []Member(mCollection), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) {
|
||||||
|
urls, err := types.NewURLs([]string{peerURL})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &membersAPIActionAdd{peerURLs: urls}
|
||||||
|
resp, body, err := m.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
var merr membersError
|
||||||
|
if err := json.Unmarshal(body, &merr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, merr
|
||||||
|
}
|
||||||
|
|
||||||
|
var memb Member
|
||||||
|
if err := json.Unmarshal(body, &memb); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &memb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *httpMembersAPI) Update(ctx context.Context, memberID string, peerURLs []string) error {
|
||||||
|
urls, err := types.NewURLs(peerURLs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &membersAPIActionUpdate{peerURLs: urls, memberID: memberID}
|
||||||
|
resp, body, err := m.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusNotFound, http.StatusConflict); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNoContent {
|
||||||
|
var merr membersError
|
||||||
|
if err := json.Unmarshal(body, &merr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return merr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *httpMembersAPI) Remove(ctx context.Context, memberID string) error {
|
||||||
|
req := &membersAPIActionRemove{memberID: memberID}
|
||||||
|
resp, _, err := m.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusGone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *httpMembersAPI) Leader(ctx context.Context) (*Member, error) {
|
||||||
|
req := &membersAPIActionLeader{}
|
||||||
|
resp, body, err := m.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var leader Member
|
||||||
|
if err := json.Unmarshal(body, &leader); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &leader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersAPIActionList struct{}
|
||||||
|
|
||||||
|
func (l *membersAPIActionList) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2MembersURL(ep)
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersAPIActionRemove struct {
|
||||||
|
memberID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *membersAPIActionRemove) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2MembersURL(ep)
|
||||||
|
u.Path = path.Join(u.Path, d.memberID)
|
||||||
|
req, _ := http.NewRequest("DELETE", u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersAPIActionAdd struct {
|
||||||
|
peerURLs types.URLs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *membersAPIActionAdd) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2MembersURL(ep)
|
||||||
|
m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
|
||||||
|
b, _ := json.Marshal(&m)
|
||||||
|
req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(b))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersAPIActionUpdate struct {
|
||||||
|
memberID string
|
||||||
|
peerURLs types.URLs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *membersAPIActionUpdate) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2MembersURL(ep)
|
||||||
|
m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
|
||||||
|
u.Path = path.Join(u.Path, a.memberID)
|
||||||
|
b, _ := json.Marshal(&m)
|
||||||
|
req, _ := http.NewRequest("PUT", u.String(), bytes.NewReader(b))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertStatusCode(got int, want ...int) (err error) {
|
||||||
|
for _, w := range want {
|
||||||
|
if w == got {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unexpected status code %d", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersAPIActionLeader struct{}
|
||||||
|
|
||||||
|
func (l *membersAPIActionLeader) HTTPRequest(ep url.URL) *http.Request {
|
||||||
|
u := v2MembersURL(ep)
|
||||||
|
u.Path = path.Join(u.Path, defaultLeaderSuffix)
|
||||||
|
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
// v2MembersURL add the necessary path to the provided endpoint
|
||||||
|
// to route requests to the default v2 members API.
|
||||||
|
func v2MembersURL(ep url.URL) *url.URL {
|
||||||
|
ep.Path = path.Join(ep.Path, defaultV2MembersPrefix)
|
||||||
|
return &ep
|
||||||
|
}
|
||||||
|
|
||||||
|
type membersError struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Code int `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e membersError) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
65
vendor/github.com/coreos/etcd/client/srv.go
generated
vendored
Normal file
65
vendor/github.com/coreos/etcd/client/srv.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// indirection for testing
|
||||||
|
lookupSRV = net.LookupSRV
|
||||||
|
)
|
||||||
|
|
||||||
|
type srvDiscover struct{}
|
||||||
|
|
||||||
|
// NewSRVDiscover constructs a new Discoverer that uses the stdlib to lookup SRV records.
|
||||||
|
func NewSRVDiscover() Discoverer {
|
||||||
|
return &srvDiscover{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover looks up the etcd servers for the domain.
|
||||||
|
func (d *srvDiscover) Discover(domain string) ([]string, error) {
|
||||||
|
var urls []*url.URL
|
||||||
|
|
||||||
|
updateURLs := func(service, scheme string) error {
|
||||||
|
_, addrs, err := lookupSRV(service, "tcp", domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, srv := range addrs {
|
||||||
|
urls = append(urls, &url.URL{
|
||||||
|
Scheme: scheme,
|
||||||
|
Host: net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
errHTTPS := updateURLs("etcd-client-ssl", "https")
|
||||||
|
errHTTP := updateURLs("etcd-client", "http")
|
||||||
|
|
||||||
|
if errHTTPS != nil && errHTTP != nil {
|
||||||
|
return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints := make([]string, len(urls))
|
||||||
|
for i := range urls {
|
||||||
|
endpoints[i] = urls[i].String()
|
||||||
|
}
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
23
vendor/github.com/coreos/etcd/client/util.go
generated
vendored
Normal file
23
vendor/github.com/coreos/etcd/client/util.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// 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 client
|
||||||
|
|
||||||
|
// IsKeyNotFound returns true if the error code is ErrorCodeKeyNotFound.
|
||||||
|
func IsKeyNotFound(err error) bool {
|
||||||
|
if cErr, ok := err.(Error); ok {
|
||||||
|
return cErr.Code == ErrorCodeKeyNotFound
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
77
vendor/github.com/coreos/etcd/clientv3/README.md
generated
vendored
Normal file
77
vendor/github.com/coreos/etcd/clientv3/README.md
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# etcd/clientv3
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/coreos/etcd/clientv3)
|
||||||
|
|
||||||
|
`etcd/clientv3` is the official Go etcd client for v3.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/coreos/etcd/clientv3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get started
|
||||||
|
|
||||||
|
Create client using `clientv3.New`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
cli, err := clientv3.New(clientv3.Config{
|
||||||
|
Endpoints: []string{"localhost:2379", "localhost:22379", "localhost:32379"},
|
||||||
|
DialTimeout: 5 * time.Second,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// handle error!
|
||||||
|
}
|
||||||
|
defer cli.Close()
|
||||||
|
```
|
||||||
|
|
||||||
|
etcd v3 uses [`gRPC`](http://www.grpc.io) for remote procedure calls. And `clientv3` uses
|
||||||
|
[`grpc-go`](https://github.com/grpc/grpc-go) to connect to etcd. Make sure to close the client after using it.
|
||||||
|
If the client is not closed, the connection will have leaky goroutines. To specify client request timeout,
|
||||||
|
pass `context.WithTimeout` to APIs:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
resp, err := kvc.Put(ctx, "sample_key", "sample_value")
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
// handle error!
|
||||||
|
}
|
||||||
|
// use the response
|
||||||
|
```
|
||||||
|
|
||||||
|
etcd uses `cmd/vendor` directory to store external dependencies, which are
|
||||||
|
to be compiled into etcd release binaries. `client` can be imported without
|
||||||
|
vendoring. For full compatibility, it is recommended to vendor builds using
|
||||||
|
etcd's vendored packages, using tools like godep, as in
|
||||||
|
[vendor directories](https://golang.org/cmd/go/#hdr-Vendor_Directories).
|
||||||
|
For more detail, please read [Go vendor design](https://golang.org/s/go15vendor).
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
etcd client returns 2 types of errors:
|
||||||
|
|
||||||
|
1. context error: canceled or deadline exceeded.
|
||||||
|
2. gRPC error: see [api/v3rpc/rpctypes](https://godoc.org/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes).
|
||||||
|
|
||||||
|
Here is the example code to handle client errors:
|
||||||
|
|
||||||
|
```go
|
||||||
|
resp, err := kvc.Put(ctx, "", "")
|
||||||
|
if err != nil {
|
||||||
|
switch err {
|
||||||
|
case context.Canceled:
|
||||||
|
log.Fatalf("ctx is canceled by another routine: %v", err)
|
||||||
|
case context.DeadlineExceeded:
|
||||||
|
log.Fatalf("ctx is attached with a deadline is exceeded: %v", err)
|
||||||
|
case rpctypes.ErrEmptyKey:
|
||||||
|
log.Fatalf("client-side error: %v", err)
|
||||||
|
default:
|
||||||
|
log.Fatalf("bad cluster endpoints, which are not etcd servers: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
More code examples can be found at [GoDoc](https://godoc.org/github.com/coreos/etcd/clientv3).
|
229
vendor/github.com/coreos/etcd/clientv3/auth.go
generated
vendored
Normal file
229
vendor/github.com/coreos/etcd/clientv3/auth.go
generated
vendored
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/auth/authpb"
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
AuthEnableResponse pb.AuthEnableResponse
|
||||||
|
AuthDisableResponse pb.AuthDisableResponse
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PermRead = authpb.READ
|
||||||
|
PermWrite = authpb.WRITE
|
||||||
|
PermReadWrite = authpb.READWRITE
|
||||||
|
)
|
||||||
|
|
||||||
|
type Auth interface {
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// UserAdd adds a new user to an etcd cluster.
|
||||||
|
UserAdd(ctx context.Context, name string, password string) (*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 auth struct {
|
||||||
|
c *Client
|
||||||
|
|
||||||
|
conn *grpc.ClientConn // conn in-use
|
||||||
|
remote pb.AuthClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuth(c *Client) Auth {
|
||||||
|
conn := c.ActiveConnection()
|
||||||
|
return &auth{
|
||||||
|
conn: c.ActiveConnection(),
|
||||||
|
remote: pb.NewAuthClient(conn),
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) AuthEnable(ctx context.Context) (*AuthEnableResponse, error) {
|
||||||
|
resp, err := auth.remote.AuthEnable(ctx, &pb.AuthEnableRequest{})
|
||||||
|
return (*AuthEnableResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) AuthDisable(ctx context.Context) (*AuthDisableResponse, error) {
|
||||||
|
resp, err := auth.remote.AuthDisable(ctx, &pb.AuthDisableRequest{})
|
||||||
|
return (*AuthDisableResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserAdd(ctx context.Context, name string, password string) (*AuthUserAddResponse, error) {
|
||||||
|
resp, err := auth.remote.UserAdd(ctx, &pb.AuthUserAddRequest{Name: name, Password: password})
|
||||||
|
return (*AuthUserAddResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserDelete(ctx context.Context, name string) (*AuthUserDeleteResponse, error) {
|
||||||
|
resp, err := auth.remote.UserDelete(ctx, &pb.AuthUserDeleteRequest{Name: name})
|
||||||
|
return (*AuthUserDeleteResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserChangePassword(ctx context.Context, name string, password string) (*AuthUserChangePasswordResponse, error) {
|
||||||
|
resp, err := auth.remote.UserChangePassword(ctx, &pb.AuthUserChangePasswordRequest{Name: name, Password: password})
|
||||||
|
return (*AuthUserChangePasswordResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserGrantRole(ctx context.Context, user string, role string) (*AuthUserGrantRoleResponse, error) {
|
||||||
|
resp, err := auth.remote.UserGrantRole(ctx, &pb.AuthUserGrantRoleRequest{User: user, Role: role})
|
||||||
|
return (*AuthUserGrantRoleResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserGet(ctx context.Context, name string) (*AuthUserGetResponse, error) {
|
||||||
|
resp, err := auth.remote.UserGet(ctx, &pb.AuthUserGetRequest{Name: name}, grpc.FailFast(false))
|
||||||
|
return (*AuthUserGetResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserList(ctx context.Context) (*AuthUserListResponse, error) {
|
||||||
|
resp, err := auth.remote.UserList(ctx, &pb.AuthUserListRequest{}, grpc.FailFast(false))
|
||||||
|
return (*AuthUserListResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) UserRevokeRole(ctx context.Context, name string, role string) (*AuthUserRevokeRoleResponse, error) {
|
||||||
|
resp, err := auth.remote.UserRevokeRole(ctx, &pb.AuthUserRevokeRoleRequest{Name: name, Role: role})
|
||||||
|
return (*AuthUserRevokeRoleResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) RoleAdd(ctx context.Context, name string) (*AuthRoleAddResponse, error) {
|
||||||
|
resp, err := auth.remote.RoleAdd(ctx, &pb.AuthRoleAddRequest{Name: name})
|
||||||
|
return (*AuthRoleAddResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) 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})
|
||||||
|
return (*AuthRoleGrantPermissionResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) RoleGet(ctx context.Context, role string) (*AuthRoleGetResponse, error) {
|
||||||
|
resp, err := auth.remote.RoleGet(ctx, &pb.AuthRoleGetRequest{Role: role}, grpc.FailFast(false))
|
||||||
|
return (*AuthRoleGetResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) RoleList(ctx context.Context) (*AuthRoleListResponse, error) {
|
||||||
|
resp, err := auth.remote.RoleList(ctx, &pb.AuthRoleListRequest{}, grpc.FailFast(false))
|
||||||
|
return (*AuthRoleListResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) RoleRevokePermission(ctx context.Context, role string, key, rangeEnd string) (*AuthRoleRevokePermissionResponse, error) {
|
||||||
|
resp, err := auth.remote.RoleRevokePermission(ctx, &pb.AuthRoleRevokePermissionRequest{Role: role, Key: key, RangeEnd: rangeEnd})
|
||||||
|
return (*AuthRoleRevokePermissionResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *auth) RoleDelete(ctx context.Context, role string) (*AuthRoleDeleteResponse, error) {
|
||||||
|
resp, err := auth.remote.RoleDelete(ctx, &pb.AuthRoleDeleteRequest{Role: role})
|
||||||
|
return (*AuthRoleDeleteResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StrToPermissionType(s string) (PermissionType, error) {
|
||||||
|
val, ok := authpb.Permission_Type_value[strings.ToUpper(s)]
|
||||||
|
if ok {
|
||||||
|
return PermissionType(val), nil
|
||||||
|
}
|
||||||
|
return PermissionType(-1), fmt.Errorf("invalid permission type: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type authenticator struct {
|
||||||
|
conn *grpc.ClientConn // conn in-use
|
||||||
|
remote pb.AuthClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *authenticator) authenticate(ctx context.Context, name string, password string) (*AuthenticateResponse, error) {
|
||||||
|
resp, err := auth.remote.Authenticate(ctx, &pb.AuthenticateRequest{Name: name, Password: password}, grpc.FailFast(false))
|
||||||
|
return (*AuthenticateResponse)(resp), toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (auth *authenticator) close() {
|
||||||
|
auth.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthenticator(endpoint string, opts []grpc.DialOption) (*authenticator, error) {
|
||||||
|
conn, err := grpc.Dial(endpoint, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &authenticator{
|
||||||
|
conn: conn,
|
||||||
|
remote: pb.NewAuthClient(conn),
|
||||||
|
}, nil
|
||||||
|
}
|
147
vendor/github.com/coreos/etcd/clientv3/balancer.go
generated
vendored
Normal file
147
vendor/github.com/coreos/etcd/clientv3/balancer.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// 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 (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// simpleBalancer does the bare minimum to expose multiple eps
|
||||||
|
// to the grpc reconnection code path
|
||||||
|
type simpleBalancer struct {
|
||||||
|
// addrs are the client's endpoints for grpc
|
||||||
|
addrs []grpc.Address
|
||||||
|
// notifyCh notifies grpc of the set of addresses for connecting
|
||||||
|
notifyCh chan []grpc.Address
|
||||||
|
|
||||||
|
// readyc closes once the first connection is up
|
||||||
|
readyc chan struct{}
|
||||||
|
readyOnce sync.Once
|
||||||
|
|
||||||
|
// mu protects upEps, pinAddr, and connectingAddr
|
||||||
|
mu sync.RWMutex
|
||||||
|
// upEps holds the current endpoints that have an active connection
|
||||||
|
upEps map[string]struct{}
|
||||||
|
// upc closes when upEps transitions from empty to non-zero or the balancer closes.
|
||||||
|
upc chan struct{}
|
||||||
|
|
||||||
|
// pinAddr is the currently pinned address; set to the empty string on
|
||||||
|
// intialization and shutdown.
|
||||||
|
pinAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSimpleBalancer(eps []string) *simpleBalancer {
|
||||||
|
notifyCh := make(chan []grpc.Address, 1)
|
||||||
|
addrs := make([]grpc.Address, len(eps))
|
||||||
|
for i := range eps {
|
||||||
|
addrs[i].Addr = getHost(eps[i])
|
||||||
|
}
|
||||||
|
notifyCh <- addrs
|
||||||
|
sb := &simpleBalancer{
|
||||||
|
addrs: addrs,
|
||||||
|
notifyCh: notifyCh,
|
||||||
|
readyc: make(chan struct{}),
|
||||||
|
upEps: make(map[string]struct{}),
|
||||||
|
upc: make(chan struct{}),
|
||||||
|
}
|
||||||
|
return sb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBalancer) Start(target string) error { return nil }
|
||||||
|
|
||||||
|
func (b *simpleBalancer) ConnectNotify() <-chan struct{} {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
return b.upc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBalancer) Up(addr grpc.Address) func(error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
if len(b.upEps) == 0 {
|
||||||
|
// notify waiting Get()s and pin first connected address
|
||||||
|
close(b.upc)
|
||||||
|
b.pinAddr = addr.Addr
|
||||||
|
}
|
||||||
|
b.upEps[addr.Addr] = struct{}{}
|
||||||
|
b.mu.Unlock()
|
||||||
|
// notify client that a connection is up
|
||||||
|
b.readyOnce.Do(func() { close(b.readyc) })
|
||||||
|
return func(err error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
delete(b.upEps, addr.Addr)
|
||||||
|
if len(b.upEps) == 0 && b.pinAddr != "" {
|
||||||
|
b.upc = make(chan struct{})
|
||||||
|
} else if b.pinAddr == addr.Addr {
|
||||||
|
// choose new random up endpoint
|
||||||
|
for k := range b.upEps {
|
||||||
|
b.pinAddr = k
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBalancer) Get(ctx context.Context, opts grpc.BalancerGetOptions) (grpc.Address, func(), error) {
|
||||||
|
var addr string
|
||||||
|
for {
|
||||||
|
b.mu.RLock()
|
||||||
|
ch := b.upc
|
||||||
|
b.mu.RUnlock()
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return grpc.Address{Addr: ""}, nil, ctx.Err()
|
||||||
|
}
|
||||||
|
b.mu.RLock()
|
||||||
|
addr = b.pinAddr
|
||||||
|
upEps := len(b.upEps)
|
||||||
|
b.mu.RUnlock()
|
||||||
|
if addr == "" {
|
||||||
|
return grpc.Address{Addr: ""}, nil, grpc.ErrClientConnClosing
|
||||||
|
}
|
||||||
|
if upEps > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grpc.Address{Addr: addr}, func() {}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBalancer) Notify() <-chan []grpc.Address { return b.notifyCh }
|
||||||
|
|
||||||
|
func (b *simpleBalancer) Close() error {
|
||||||
|
b.mu.Lock()
|
||||||
|
close(b.notifyCh)
|
||||||
|
// terminate all waiting Get()s
|
||||||
|
b.pinAddr = ""
|
||||||
|
if len(b.upEps) == 0 {
|
||||||
|
close(b.upc)
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHost(ep string) string {
|
||||||
|
url, uerr := url.Parse(ep)
|
||||||
|
if uerr != nil || !strings.Contains(ep, "://") {
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
return url.Host
|
||||||
|
}
|
324
vendor/github.com/coreos/etcd/clientv3/client.go
generated
vendored
Normal file
324
vendor/github.com/coreos/etcd/clientv3/client.go
generated
vendored
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
// 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 (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoAvailableEndpoints = errors.New("etcdclient: no available endpoints")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client provides and manages an etcd v3 client session.
|
||||||
|
type Client struct {
|
||||||
|
Cluster
|
||||||
|
KV
|
||||||
|
Lease
|
||||||
|
Watcher
|
||||||
|
Auth
|
||||||
|
Maintenance
|
||||||
|
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
cfg Config
|
||||||
|
creds *credentials.TransportCredentials
|
||||||
|
balancer *simpleBalancer
|
||||||
|
retryWrapper retryRpcFunc
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// Username is a username for authentication
|
||||||
|
Username string
|
||||||
|
// Password is a password for authentication
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromURL creates a new etcdv3 client from a URL.
|
||||||
|
func NewFromURL(url string) (*Client, error) {
|
||||||
|
return New(Config{Endpoints: []string{url}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromConfigFile creates a new etcdv3 client from a configuration file.
|
||||||
|
func NewFromConfigFile(path string) (*Client, error) {
|
||||||
|
cfg, err := configFromFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return New(*cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close shuts down the client's etcd connections.
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
c.cancel()
|
||||||
|
return toErr(c.ctx, c.conn.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 { return c.cfg.Endpoints }
|
||||||
|
|
||||||
|
type authTokenCredential struct {
|
||||||
|
token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cred authTokenCredential) RequireTransportSecurity() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cred authTokenCredential) GetRequestMetadata(ctx context.Context, s ...string) (map[string]string, error) {
|
||||||
|
return map[string]string{
|
||||||
|
"token": cred.token,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) dialTarget(endpoint string) (proto string, host string, creds *credentials.TransportCredentials) {
|
||||||
|
proto = "tcp"
|
||||||
|
host = endpoint
|
||||||
|
creds = c.creds
|
||||||
|
url, uerr := url.Parse(endpoint)
|
||||||
|
if uerr != nil || !strings.Contains(endpoint, "://") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// strip scheme:// prefix since grpc dials by host
|
||||||
|
host = url.Host
|
||||||
|
switch url.Scheme {
|
||||||
|
case "unix":
|
||||||
|
proto = "unix"
|
||||||
|
case "http":
|
||||||
|
creds = nil
|
||||||
|
case "https":
|
||||||
|
if creds != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tlsconfig := &tls.Config{}
|
||||||
|
emptyCreds := credentials.NewTLS(tlsconfig)
|
||||||
|
creds = &emptyCreds
|
||||||
|
default:
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialSetupOpts gives the dial opts prior to any authentication
|
||||||
|
func (c *Client) dialSetupOpts(endpoint string, dopts ...grpc.DialOption) (opts []grpc.DialOption) {
|
||||||
|
if c.cfg.DialTimeout > 0 {
|
||||||
|
opts = []grpc.DialOption{grpc.WithTimeout(c.cfg.DialTimeout)}
|
||||||
|
}
|
||||||
|
opts = append(opts, dopts...)
|
||||||
|
|
||||||
|
// grpc issues TLS cert checks using the string passed into dial so
|
||||||
|
// that string must be the host. To recover the full scheme://host URL,
|
||||||
|
// have a map from hosts to the original endpoint.
|
||||||
|
host2ep := make(map[string]string)
|
||||||
|
for i := range c.cfg.Endpoints {
|
||||||
|
_, host, _ := c.dialTarget(c.cfg.Endpoints[i])
|
||||||
|
host2ep[host] = c.cfg.Endpoints[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
f := func(host string, t time.Duration) (net.Conn, error) {
|
||||||
|
proto, host, _ := c.dialTarget(host2ep[host])
|
||||||
|
if proto == "" {
|
||||||
|
return nil, fmt.Errorf("unknown scheme for %q", host)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return nil, c.ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return net.DialTimeout(proto, host, t)
|
||||||
|
}
|
||||||
|
opts = append(opts, grpc.WithDialer(f))
|
||||||
|
|
||||||
|
_, _, creds := c.dialTarget(endpoint)
|
||||||
|
if creds != nil {
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(*creds))
|
||||||
|
} else {
|
||||||
|
opts = append(opts, grpc.WithInsecure())
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to a single endpoint using the client's config.
|
||||||
|
func (c *Client) Dial(endpoint string) (*grpc.ClientConn, error) {
|
||||||
|
return c.dial(endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) dial(endpoint string, dopts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
opts := c.dialSetupOpts(endpoint, dopts...)
|
||||||
|
host := getHost(endpoint)
|
||||||
|
if c.Username != "" && c.Password != "" {
|
||||||
|
// use dial options without dopts to avoid reusing the client balancer
|
||||||
|
auth, err := newAuthenticator(host, c.dialSetupOpts(endpoint))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer auth.close()
|
||||||
|
|
||||||
|
resp, err := auth.authenticate(c.ctx, c.Username, c.Password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts = append(opts, grpc.WithPerRPCCredentials(authTokenCredential{token: resp.Token}))
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := grpc.Dial(host, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRequireLeader requires client requests to only succeed
|
||||||
|
// when the cluster has a leader.
|
||||||
|
func WithRequireLeader(ctx context.Context) context.Context {
|
||||||
|
md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
|
||||||
|
return metadata.NewContext(ctx, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClient(cfg *Config) (*Client, error) {
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = &Config{}
|
||||||
|
}
|
||||||
|
var creds *credentials.TransportCredentials
|
||||||
|
if cfg.TLS != nil {
|
||||||
|
c := credentials.NewTLS(cfg.TLS)
|
||||||
|
creds = &c
|
||||||
|
}
|
||||||
|
|
||||||
|
// use a temporary skeleton client to bootstrap first connection
|
||||||
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
|
client := &Client{
|
||||||
|
conn: nil,
|
||||||
|
cfg: *cfg,
|
||||||
|
creds: creds,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
}
|
||||||
|
if cfg.Username != "" && cfg.Password != "" {
|
||||||
|
client.Username = cfg.Username
|
||||||
|
client.Password = cfg.Password
|
||||||
|
}
|
||||||
|
|
||||||
|
client.balancer = newSimpleBalancer(cfg.Endpoints)
|
||||||
|
conn, err := client.dial(cfg.Endpoints[0], grpc.WithBalancer(client.balancer))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client.conn = conn
|
||||||
|
client.retryWrapper = client.newRetryWrapper()
|
||||||
|
|
||||||
|
// wait for a connection
|
||||||
|
if cfg.DialTimeout > 0 {
|
||||||
|
hasConn := false
|
||||||
|
waitc := time.After(cfg.DialTimeout)
|
||||||
|
select {
|
||||||
|
case <-client.balancer.readyc:
|
||||||
|
hasConn = true
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-waitc:
|
||||||
|
}
|
||||||
|
if !hasConn {
|
||||||
|
client.cancel()
|
||||||
|
conn.Close()
|
||||||
|
return nil, grpc.ErrClientConnTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Cluster = NewCluster(client)
|
||||||
|
client.KV = NewKV(client)
|
||||||
|
client.Lease = NewLease(client)
|
||||||
|
client.Watcher = NewWatcher(client)
|
||||||
|
client.Auth = NewAuth(client)
|
||||||
|
client.Maintenance = NewMaintenance(client)
|
||||||
|
if cfg.Logger != nil {
|
||||||
|
logger.Set(cfg.Logger)
|
||||||
|
} else {
|
||||||
|
// disable client side grpc by default
|
||||||
|
logger.Set(log.New(ioutil.Discard, "", 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
eErr := rpctypes.Error(err)
|
||||||
|
if _, ok := eErr.(rpctypes.EtcdError); ok {
|
||||||
|
return eErr != rpctypes.ErrStopped && eErr != rpctypes.ErrNoLeader
|
||||||
|
}
|
||||||
|
// treat etcdserver errors not recognized by the client as halting
|
||||||
|
return isConnClosing(err) || strings.Contains(err.Error(), "etcdserver:")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isConnClosing returns true if the error matches a grpc client closing error
|
||||||
|
func isConnClosing(err error) bool {
|
||||||
|
return strings.Contains(err.Error(), grpc.ErrClientConnClosing.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func toErr(ctx context.Context, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = rpctypes.Error(err)
|
||||||
|
switch {
|
||||||
|
case ctx.Err() != nil && strings.Contains(err.Error(), "context"):
|
||||||
|
err = ctx.Err()
|
||||||
|
case strings.Contains(err.Error(), ErrNoAvailableEndpoints.Error()):
|
||||||
|
err = ErrNoAvailableEndpoints
|
||||||
|
case strings.Contains(err.Error(), grpc.ErrClientConnClosing.Error()):
|
||||||
|
err = grpc.ErrClientConnClosing
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
102
vendor/github.com/coreos/etcd/clientv3/cluster.go
generated
vendored
Normal file
102
vendor/github.com/coreos/etcd/clientv3/cluster.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// 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 "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Member pb.Member
|
||||||
|
MemberListResponse pb.MemberListResponse
|
||||||
|
MemberAddResponse pb.MemberAddResponse
|
||||||
|
MemberRemoveResponse pb.MemberRemoveResponse
|
||||||
|
MemberUpdateResponse pb.MemberUpdateResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cluster struct {
|
||||||
|
remote pb.ClusterClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCluster(c *Client) Cluster {
|
||||||
|
return &cluster{remote: RetryClusterClient(c)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error) {
|
||||||
|
r := &pb.MemberAddRequest{PeerURLs: peerAddrs}
|
||||||
|
resp, err := c.remote.MemberAdd(ctx, r)
|
||||||
|
if err == nil {
|
||||||
|
return (*MemberAddResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cluster) MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error) {
|
||||||
|
r := &pb.MemberRemoveRequest{ID: id}
|
||||||
|
resp, err := c.remote.MemberRemove(ctx, r)
|
||||||
|
if err == nil {
|
||||||
|
return (*MemberRemoveResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cluster) MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error) {
|
||||||
|
// it is safe to retry on update.
|
||||||
|
for {
|
||||||
|
r := &pb.MemberUpdateRequest{ID: id, PeerURLs: peerAddrs}
|
||||||
|
resp, err := c.remote.MemberUpdate(ctx, r)
|
||||||
|
if err == nil {
|
||||||
|
return (*MemberUpdateResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cluster) MemberList(ctx context.Context) (*MemberListResponse, error) {
|
||||||
|
// it is safe to retry on list.
|
||||||
|
for {
|
||||||
|
resp, err := c.remote.MemberList(ctx, &pb.MemberListRequest{}, grpc.FailFast(false))
|
||||||
|
if err == nil {
|
||||||
|
return (*MemberListResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
vendor/github.com/coreos/etcd/clientv3/compact_op.go
generated
vendored
Normal file
53
vendor/github.com/coreos/etcd/clientv3/compact_op.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// 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 "github.com/coreos/etcd/etcdserver/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 RPC call wait until
|
||||||
|
// the compaction is physically applied to the local database
|
||||||
|
// such that compacted entries are totally removed from the
|
||||||
|
// backend database.
|
||||||
|
func WithCompactPhysical() CompactOption {
|
||||||
|
return func(op *CompactOp) { op.physical = true }
|
||||||
|
}
|
91
vendor/github.com/coreos/etcd/clientv3/compare.go
generated
vendored
Normal file
91
vendor/github.com/coreos/etcd/clientv3/compare.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
// 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 "github.com/coreos/etcd/etcdserver/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_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)}
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
110
vendor/github.com/coreos/etcd/clientv3/config.go
generated
vendored
Normal file
110
vendor/github.com/coreos/etcd/clientv3/config.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// 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 (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/tlsutil"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// Endpoints is a list of URLs
|
||||||
|
Endpoints []string
|
||||||
|
|
||||||
|
// DialTimeout is the timeout for failing to establish a connection.
|
||||||
|
DialTimeout time.Duration
|
||||||
|
|
||||||
|
// TLS holds the client secure credentials, if any.
|
||||||
|
TLS *tls.Config
|
||||||
|
|
||||||
|
// Logger is the logger used by client library.
|
||||||
|
Logger Logger
|
||||||
|
|
||||||
|
// Username is a username for authentication
|
||||||
|
Username string
|
||||||
|
|
||||||
|
// Password is a password for authentication
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
type yamlConfig struct {
|
||||||
|
Endpoints []string `json:"endpoints"`
|
||||||
|
DialTimeout time.Duration `json:"dial-timeout"`
|
||||||
|
InsecureTransport bool `json:"insecure-transport"`
|
||||||
|
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify"`
|
||||||
|
Certfile string `json:"cert-file"`
|
||||||
|
Keyfile string `json:"key-file"`
|
||||||
|
CAfile string `json:"ca-file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func configFromFile(fpath string) (*Config, error) {
|
||||||
|
b, err := ioutil.ReadFile(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
yc := &yamlConfig{}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(b, yc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &Config{
|
||||||
|
Endpoints: yc.Endpoints,
|
||||||
|
DialTimeout: yc.DialTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
if yc.InsecureTransport {
|
||||||
|
cfg.TLS = nil
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cert *tls.Certificate
|
||||||
|
cp *x509.CertPool
|
||||||
|
)
|
||||||
|
|
||||||
|
if yc.Certfile != "" && yc.Keyfile != "" {
|
||||||
|
cert, err = tlsutil.NewCert(yc.Certfile, yc.Keyfile, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if yc.CAfile != "" {
|
||||||
|
cp, err = tlsutil.NewCertPool([]string{yc.CAfile})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlscfg := &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS10,
|
||||||
|
InsecureSkipVerify: yc.InsecureSkipTLSVerify,
|
||||||
|
RootCAs: cp,
|
||||||
|
}
|
||||||
|
if cert != nil {
|
||||||
|
tlscfg.Certificates = []tls.Certificate{*cert}
|
||||||
|
}
|
||||||
|
cfg.TLS = tlscfg
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
64
vendor/github.com/coreos/etcd/clientv3/doc.go
generated
vendored
Normal file
64
vendor/github.com/coreos/etcd/clientv3/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// 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`:
|
||||||
|
//
|
||||||
|
// 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 client request timeout, pass context.WithTimeout to APIs:
|
||||||
|
//
|
||||||
|
// 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: see https://github.com/coreos/etcd/blob/master/etcdserver/api/v3rpc/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 verr, ok := err.(*v3rpc.ErrEmptyKey); ok {
|
||||||
|
// // process (verr.Errors)
|
||||||
|
// } else {
|
||||||
|
// // bad cluster endpoints, which are not etcd servers
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
package clientv3
|
176
vendor/github.com/coreos/etcd/clientv3/kv.go
generated
vendored
Normal file
176
vendor/github.com/coreos/etcd/clientv3/kv.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
// 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 (
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"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 declaring operations to be issued at a later time
|
||||||
|
// whereas Get/Put/Delete are for better suited for when the operation
|
||||||
|
// should be immediately issued at time of declaration.
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op OpResponse) Put() *PutResponse { return op.put }
|
||||||
|
func (op OpResponse) Get() *GetResponse { return op.get }
|
||||||
|
func (op OpResponse) Del() *DeleteResponse { return op.del }
|
||||||
|
|
||||||
|
type kv struct {
|
||||||
|
remote pb.KVClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKV(c *Client) KV {
|
||||||
|
return &kv{remote: RetryKVClient(c)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) {
|
||||||
|
r, err := kv.Do(ctx, OpPut(key, val, opts...))
|
||||||
|
return r.put, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) {
|
||||||
|
r, err := kv.Do(ctx, OpGet(key, opts...))
|
||||||
|
return r.get, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
|
||||||
|
r, err := kv.Do(ctx, OpDelete(key, opts...))
|
||||||
|
return r.del, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
|
||||||
|
resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest(), grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
return (*CompactResponse)(resp), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Txn(ctx context.Context) Txn {
|
||||||
|
return &txn{
|
||||||
|
kv: kv,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) {
|
||||||
|
for {
|
||||||
|
resp, err := kv.do(ctx, op)
|
||||||
|
if err == nil {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return resp, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
// do not retry on modifications
|
||||||
|
if op.isWrite() {
|
||||||
|
return resp, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
|
||||||
|
var err error
|
||||||
|
switch op.t {
|
||||||
|
// TODO: handle other ops
|
||||||
|
case tRange:
|
||||||
|
var resp *pb.RangeResponse
|
||||||
|
r := &pb.RangeRequest{
|
||||||
|
Key: op.key,
|
||||||
|
RangeEnd: op.end,
|
||||||
|
Limit: op.limit,
|
||||||
|
Revision: op.rev,
|
||||||
|
Serializable: op.serializable,
|
||||||
|
KeysOnly: op.keysOnly,
|
||||||
|
CountOnly: op.countOnly,
|
||||||
|
}
|
||||||
|
if op.sort != nil {
|
||||||
|
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
|
||||||
|
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = kv.remote.Range(ctx, r, grpc.FailFast(false))
|
||||||
|
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)}
|
||||||
|
resp, err = kv.remote.Put(ctx, r)
|
||||||
|
if err == nil {
|
||||||
|
return OpResponse{put: (*PutResponse)(resp)}, nil
|
||||||
|
}
|
||||||
|
case tDeleteRange:
|
||||||
|
var resp *pb.DeleteRangeResponse
|
||||||
|
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end}
|
||||||
|
resp, err = kv.remote.DeleteRange(ctx, r)
|
||||||
|
if err == nil {
|
||||||
|
return OpResponse{del: (*DeleteResponse)(resp)}, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("Unknown op")
|
||||||
|
}
|
||||||
|
return OpResponse{}, err
|
||||||
|
}
|
462
vendor/github.com/coreos/etcd/clientv3/lease.go
generated
vendored
Normal file
462
vendor/github.com/coreos/etcd/clientv3/lease.go
generated
vendored
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
// 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 (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
LeaseRevokeResponse pb.LeaseRevokeResponse
|
||||||
|
LeaseID int64
|
||||||
|
)
|
||||||
|
|
||||||
|
// LeaseGrantResponse is used to convert the protobuf grant response.
|
||||||
|
type LeaseGrantResponse struct {
|
||||||
|
*pb.ResponseHeader
|
||||||
|
ID LeaseID
|
||||||
|
TTL int64
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaseKeepAliveResponse is used to convert the protobuf keepalive response.
|
||||||
|
type LeaseKeepAliveResponse struct {
|
||||||
|
*pb.ResponseHeader
|
||||||
|
ID LeaseID
|
||||||
|
TTL int64
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// a small buffer to store unsent lease responses.
|
||||||
|
leaseResponseChSize = 16
|
||||||
|
// NoLease is a lease ID for the absence of a lease.
|
||||||
|
NoLease LeaseID = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// KeepAlive keeps the given lease alive forever.
|
||||||
|
KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error)
|
||||||
|
|
||||||
|
// KeepAliveOnce renews the lease once. 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 when recvKeepAliveLoop stops
|
||||||
|
donec chan struct{}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
l := &lessor{
|
||||||
|
donec: make(chan struct{}),
|
||||||
|
keepAlives: make(map[LeaseID]*keepAlive),
|
||||||
|
remote: RetryLeaseClient(c),
|
||||||
|
firstKeepAliveTimeout: c.cfg.DialTimeout + time.Second,
|
||||||
|
}
|
||||||
|
if l.firstKeepAliveTimeout == time.Second {
|
||||||
|
l.firstKeepAliveTimeout = defaultTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
l.stopCtx, l.stopCancel = context.WithCancel(context.Background())
|
||||||
|
go l.recvKeepAliveLoop()
|
||||||
|
go l.deadlineLoop()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) Grant(ctx context.Context, ttl int64) (*LeaseGrantResponse, error) {
|
||||||
|
cctx, cancel := context.WithCancel(ctx)
|
||||||
|
done := cancelWhenStop(cancel, l.stopCtx.Done())
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
for {
|
||||||
|
r := &pb.LeaseGrantRequest{TTL: ttl}
|
||||||
|
resp, err := l.remote.LeaseGrant(cctx, r)
|
||||||
|
if err == nil {
|
||||||
|
gresp := &LeaseGrantResponse{
|
||||||
|
ResponseHeader: resp.GetHeader(),
|
||||||
|
ID: LeaseID(resp.ID),
|
||||||
|
TTL: resp.TTL,
|
||||||
|
Error: resp.Error,
|
||||||
|
}
|
||||||
|
return gresp, nil
|
||||||
|
}
|
||||||
|
if isHaltErr(cctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
if nerr := l.newStream(); nerr != nil {
|
||||||
|
return nil, nerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) Revoke(ctx context.Context, id LeaseID) (*LeaseRevokeResponse, error) {
|
||||||
|
cctx, cancel := context.WithCancel(ctx)
|
||||||
|
done := cancelWhenStop(cancel, l.stopCtx.Done())
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
for {
|
||||||
|
r := &pb.LeaseRevokeRequest{ID: int64(id)}
|
||||||
|
resp, err := l.remote.LeaseRevoke(cctx, r)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return (*LeaseRevokeResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
if nerr := l.newStream(); nerr != nil {
|
||||||
|
return nil, nerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) KeepAlive(ctx context.Context, id LeaseID) (<-chan *LeaseKeepAliveResponse, error) {
|
||||||
|
ch := make(chan *LeaseKeepAliveResponse, leaseResponseChSize)
|
||||||
|
|
||||||
|
l.mu.Lock()
|
||||||
|
ka, ok := l.keepAlives[id]
|
||||||
|
if !ok {
|
||||||
|
// create fresh keep alive
|
||||||
|
ka = &keepAlive{
|
||||||
|
chs: []chan<- *LeaseKeepAliveResponse{ch},
|
||||||
|
ctxs: []context.Context{ctx},
|
||||||
|
deadline: time.Now().Add(l.firstKeepAliveTimeout),
|
||||||
|
nextKeepAlive: time.Now(),
|
||||||
|
donec: make(chan struct{}),
|
||||||
|
}
|
||||||
|
l.keepAlives[id] = ka
|
||||||
|
} else {
|
||||||
|
// add channel and context to existing keep alive
|
||||||
|
ka.ctxs = append(ka.ctxs, ctx)
|
||||||
|
ka.chs = append(ka.chs, ch)
|
||||||
|
}
|
||||||
|
l.mu.Unlock()
|
||||||
|
|
||||||
|
go l.keepAliveCtxCloser(id, ctx, ka.donec)
|
||||||
|
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) KeepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error) {
|
||||||
|
cctx, cancel := context.WithCancel(ctx)
|
||||||
|
done := cancelWhenStop(cancel, l.stopCtx.Done())
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
for {
|
||||||
|
resp, err := l.keepAliveOnce(cctx, id)
|
||||||
|
if err == nil {
|
||||||
|
if resp.TTL == 0 {
|
||||||
|
err = rpctypes.ErrLeaseNotFound
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nerr := l.newStream(); nerr != nil {
|
||||||
|
return nil, nerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) Close() error {
|
||||||
|
l.stopCancel()
|
||||||
|
<-l.donec
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) keepAliveCtxCloser(id LeaseID, ctx context.Context, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) keepAliveOnce(ctx context.Context, id LeaseID) (*LeaseKeepAliveResponse, error) {
|
||||||
|
cctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
stream, err := l.remote.LeaseKeepAlive(cctx, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stream.Send(&pb.LeaseKeepAliveRequest{ID: int64(id)})
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, rerr := stream.Recv()
|
||||||
|
if rerr != nil {
|
||||||
|
return nil, toErr(ctx, rerr)
|
||||||
|
}
|
||||||
|
|
||||||
|
karesp := &LeaseKeepAliveResponse{
|
||||||
|
ResponseHeader: resp.GetHeader(),
|
||||||
|
ID: LeaseID(resp.ID),
|
||||||
|
TTL: resp.TTL,
|
||||||
|
}
|
||||||
|
return karesp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) recvKeepAliveLoop() {
|
||||||
|
defer func() {
|
||||||
|
l.mu.Lock()
|
||||||
|
close(l.donec)
|
||||||
|
for _, ka := range l.keepAlives {
|
||||||
|
ka.Close()
|
||||||
|
}
|
||||||
|
l.keepAlives = make(map[LeaseID]*keepAlive)
|
||||||
|
l.mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
stream, serr := l.resetRecv()
|
||||||
|
for serr == nil {
|
||||||
|
resp, err := stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
if isHaltErr(l.stopCtx, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stream, serr = l.resetRecv()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
l.recvKeepAlive(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetRecv opens a new lease stream and starts sending LeaseKeepAliveRequests
|
||||||
|
func (l *lessor) resetRecv() (pb.Lease_LeaseKeepAliveClient, error) {
|
||||||
|
if err := l.newStream(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stream := l.getKeepAliveStream()
|
||||||
|
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(1 + time.Duration(karesp.TTL/3)*time.Second)
|
||||||
|
ka.deadline = time.Now().Add(time.Duration(karesp.TTL) * time.Second)
|
||||||
|
for _, ch := range ka.chs {
|
||||||
|
select {
|
||||||
|
case ch <- karesp:
|
||||||
|
ka.nextKeepAlive = nextKeepAlive
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 LeaseKeepAliveRequests for the lifetime of a lease stream
|
||||||
|
func (l *lessor) sendKeepAliveLoop(stream pb.Lease_LeaseKeepAliveClient) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
case <-stream.Context().Done():
|
||||||
|
return
|
||||||
|
case <-l.donec:
|
||||||
|
return
|
||||||
|
case <-l.stopCtx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tosend := make([]LeaseID, 0)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) getKeepAliveStream() pb.Lease_LeaseKeepAliveClient {
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
return l.stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lessor) newStream() error {
|
||||||
|
sctx, cancel := context.WithCancel(l.stopCtx)
|
||||||
|
stream, err := l.remote.LeaseKeepAlive(sctx, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return toErr(sctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.mu.Lock()
|
||||||
|
defer l.mu.Unlock()
|
||||||
|
if l.stream != nil && l.streamCancel != nil {
|
||||||
|
l.stream.CloseSend()
|
||||||
|
l.streamCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
l.streamCancel = cancel
|
||||||
|
l.stream = stream
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ka *keepAlive) Close() {
|
||||||
|
close(ka.donec)
|
||||||
|
for _, ch := range ka.chs {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancelWhenStop calls cancel when the given stopc fires. It returns a done chan. done
|
||||||
|
// should be closed when the work is finished. When done fires, cancelWhenStop will release
|
||||||
|
// its internal resource.
|
||||||
|
func cancelWhenStop(cancel context.CancelFunc, stopc <-chan struct{}) chan<- struct{} {
|
||||||
|
done := make(chan struct{}, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-stopc:
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return done
|
||||||
|
}
|
64
vendor/github.com/coreos/etcd/clientv3/logger.go
generated
vendored
Normal file
64
vendor/github.com/coreos/etcd/clientv3/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// 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"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger grpclog.Logger
|
||||||
|
|
||||||
|
var (
|
||||||
|
logger settableLogger
|
||||||
|
)
|
||||||
|
|
||||||
|
type settableLogger struct {
|
||||||
|
l grpclog.Logger
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// use go's standard logger by default like grpc
|
||||||
|
logger.mu.Lock()
|
||||||
|
logger.l = log.New(os.Stderr, "", log.LstdFlags)
|
||||||
|
grpclog.SetLogger(&logger)
|
||||||
|
logger.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settableLogger) Set(l Logger) {
|
||||||
|
s.mu.Lock()
|
||||||
|
logger.l = l
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settableLogger) Get() Logger {
|
||||||
|
s.mu.RLock()
|
||||||
|
l := logger.l
|
||||||
|
s.mu.RUnlock()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// implement the grpclog.Logger interface
|
||||||
|
|
||||||
|
func (s *settableLogger) Fatal(args ...interface{}) { s.Get().Fatal(args...) }
|
||||||
|
func (s *settableLogger) Fatalf(format string, args ...interface{}) { s.Get().Fatalf(format, args...) }
|
||||||
|
func (s *settableLogger) Fatalln(args ...interface{}) { s.Get().Fatalln(args...) }
|
||||||
|
func (s *settableLogger) Print(args ...interface{}) { s.Get().Print(args...) }
|
||||||
|
func (s *settableLogger) Printf(format string, args ...interface{}) { s.Get().Printf(format, args...) }
|
||||||
|
func (s *settableLogger) Println(args ...interface{}) { s.Get().Println(args...) }
|
164
vendor/github.com/coreos/etcd/clientv3/maintenance.go
generated
vendored
Normal file
164
vendor/github.com/coreos/etcd/clientv3/maintenance.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// 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 (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
DefragmentResponse pb.DefragmentResponse
|
||||||
|
AlarmResponse pb.AlarmResponse
|
||||||
|
AlarmMember pb.AlarmMember
|
||||||
|
StatusResponse pb.StatusResponse
|
||||||
|
)
|
||||||
|
|
||||||
|
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 defragments storage backend of the etcd member with given endpoint.
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// Snapshot provides a reader for a snapshot of a backend.
|
||||||
|
Snapshot(ctx context.Context) (io.ReadCloser, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type maintenance struct {
|
||||||
|
c *Client
|
||||||
|
remote pb.MaintenanceClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMaintenance(c *Client) Maintenance {
|
||||||
|
return &maintenance{c: c, remote: pb.NewMaintenanceClient(c.conn)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *maintenance) AlarmList(ctx context.Context) (*AlarmResponse, error) {
|
||||||
|
req := &pb.AlarmRequest{
|
||||||
|
Action: pb.AlarmRequest_GET,
|
||||||
|
MemberID: 0, // all
|
||||||
|
Alarm: pb.AlarmType_NONE, // all
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
resp, err := m.remote.Alarm(ctx, req, grpc.FailFast(false))
|
||||||
|
if err == nil {
|
||||||
|
return (*AlarmResponse)(resp), nil
|
||||||
|
}
|
||||||
|
if isHaltErr(ctx, err) {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *maintenance) AlarmDisarm(ctx context.Context, am *AlarmMember) (*AlarmResponse, error) {
|
||||||
|
req := &pb.AlarmRequest{
|
||||||
|
Action: pb.AlarmRequest_DEACTIVATE,
|
||||||
|
MemberID: am.MemberID,
|
||||||
|
Alarm: am.Alarm,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.MemberID == 0 && req.Alarm == pb.AlarmType_NONE {
|
||||||
|
ar, err := m.AlarmList(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
ret := AlarmResponse{}
|
||||||
|
for _, am := range ar.Alarms {
|
||||||
|
dresp, derr := m.AlarmDisarm(ctx, (*AlarmMember)(am))
|
||||||
|
if derr != nil {
|
||||||
|
return nil, toErr(ctx, derr)
|
||||||
|
}
|
||||||
|
ret.Alarms = append(ret.Alarms, dresp.Alarms...)
|
||||||
|
}
|
||||||
|
return &ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := m.remote.Alarm(ctx, req, grpc.FailFast(false))
|
||||||
|
if err == nil {
|
||||||
|
return (*AlarmResponse)(resp), nil
|
||||||
|
}
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *maintenance) Defragment(ctx context.Context, endpoint string) (*DefragmentResponse, error) {
|
||||||
|
conn, err := m.c.Dial(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
remote := pb.NewMaintenanceClient(conn)
|
||||||
|
resp, err := remote.Defragment(ctx, &pb.DefragmentRequest{}, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
return (*DefragmentResponse)(resp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *maintenance) Status(ctx context.Context, endpoint string) (*StatusResponse, error) {
|
||||||
|
conn, err := m.c.Dial(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
remote := pb.NewMaintenanceClient(conn)
|
||||||
|
resp, err := remote.Status(ctx, &pb.StatusRequest{}, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
return (*StatusResponse)(resp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *maintenance) Snapshot(ctx context.Context) (io.ReadCloser, error) {
|
||||||
|
ss, err := m.remote.Snapshot(ctx, &pb.SnapshotRequest{}, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, toErr(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
resp, err := ss.Recv()
|
||||||
|
if err != nil {
|
||||||
|
pw.CloseWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp == nil && err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, werr := pw.Write(resp.Blob); werr != nil {
|
||||||
|
pw.CloseWithError(werr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pw.Close()
|
||||||
|
}()
|
||||||
|
return pr, nil
|
||||||
|
}
|
273
vendor/github.com/coreos/etcd/clientv3/op.go
generated
vendored
Normal file
273
vendor/github.com/coreos/etcd/clientv3/op.go
generated
vendored
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
// 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 "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type opType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// A default Op has opType 0, which is invalid.
|
||||||
|
tRange opType = iota + 1
|
||||||
|
tPut
|
||||||
|
tDeleteRange
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// for range, watch
|
||||||
|
rev int64
|
||||||
|
|
||||||
|
// progressNotify is for progress updates.
|
||||||
|
progressNotify bool
|
||||||
|
|
||||||
|
// for put
|
||||||
|
val []byte
|
||||||
|
leaseID LeaseID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op Op) toRequestOp() *pb.RequestOp {
|
||||||
|
switch op.t {
|
||||||
|
case tRange:
|
||||||
|
r := &pb.RangeRequest{
|
||||||
|
Key: op.key,
|
||||||
|
RangeEnd: op.end,
|
||||||
|
Limit: op.limit,
|
||||||
|
Revision: op.rev,
|
||||||
|
Serializable: op.serializable,
|
||||||
|
KeysOnly: op.keysOnly,
|
||||||
|
CountOnly: op.countOnly,
|
||||||
|
}
|
||||||
|
if op.sort != nil {
|
||||||
|
r.SortOrder = pb.RangeRequest_SortOrder(op.sort.Order)
|
||||||
|
r.SortTarget = pb.RangeRequest_SortTarget(op.sort.Target)
|
||||||
|
}
|
||||||
|
return &pb.RequestOp{Request: &pb.RequestOp_RequestRange{RequestRange: r}}
|
||||||
|
case tPut:
|
||||||
|
r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID)}
|
||||||
|
return &pb.RequestOp{Request: &pb.RequestOp_RequestPut{RequestPut: r}}
|
||||||
|
case tDeleteRange:
|
||||||
|
r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end}
|
||||||
|
return &pb.RequestOp{Request: &pb.RequestOp_RequestDeleteRange{RequestDeleteRange: r}}
|
||||||
|
default:
|
||||||
|
panic("Unknown Op")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op Op) isWrite() bool {
|
||||||
|
return op.t != tRange
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpGet(key string, opts ...OpOption) Op {
|
||||||
|
ret := Op{t: tRange, key: []byte(key)}
|
||||||
|
ret.applyOpts(opts)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpDelete(key string, opts ...OpOption) Op {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
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 delete")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
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 delete")
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
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) {
|
||||||
|
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.end = getPrefix(op.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRange specifies the range of 'Get' or 'Delete' requests.
|
||||||
|
// For example, 'Get' requests with 'WithRange(end)' returns
|
||||||
|
// the keys in the range [key, end).
|
||||||
|
func WithRange(endKey string) OpOption {
|
||||||
|
return func(op *Op) { op.end = []byte(endKey) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFromKey specifies the range of 'Get' or 'Delete' requests
|
||||||
|
// to be equal or greater than the key in the argument.
|
||||||
|
func WithFromKey() OpOption { return WithRange("\x00") }
|
||||||
|
|
||||||
|
// 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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// Progress updates have zero events in WatchResponse.
|
||||||
|
func WithProgressNotify() OpOption {
|
||||||
|
return func(op *Op) {
|
||||||
|
op.progressNotify = true
|
||||||
|
}
|
||||||
|
}
|
243
vendor/github.com/coreos/etcd/clientv3/retry.go
generated
vendored
Normal file
243
vendor/github.com/coreos/etcd/clientv3/retry.go
generated
vendored
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// 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 "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rpcFunc func(ctx context.Context) error
|
||||||
|
type retryRpcFunc func(context.Context, rpcFunc)
|
||||||
|
|
||||||
|
func (c *Client) newRetryWrapper() retryRpcFunc {
|
||||||
|
return func(rpcCtx context.Context, f rpcFunc) {
|
||||||
|
for {
|
||||||
|
err := f(rpcCtx)
|
||||||
|
// ignore grpc conn closing on fail-fast calls; they are transient errors
|
||||||
|
if err == nil || !isConnClosing(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-c.balancer.ConnectNotify():
|
||||||
|
case <-rpcCtx.Done():
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type retryKVClient struct {
|
||||||
|
pb.KVClient
|
||||||
|
retryf retryRpcFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryKVClient implements a KVClient that uses the client's FailFast retry policy.
|
||||||
|
func RetryKVClient(c *Client) pb.KVClient {
|
||||||
|
return &retryKVClient{pb.NewKVClient(c.conn), c.retryWrapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rkv *retryKVClient) Put(ctx context.Context, in *pb.PutRequest, opts ...grpc.CallOption) (resp *pb.PutResponse, err error) {
|
||||||
|
rkv.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rkv.KVClient.Put(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rkv *retryKVClient) DeleteRange(ctx context.Context, in *pb.DeleteRangeRequest, opts ...grpc.CallOption) (resp *pb.DeleteRangeResponse, err error) {
|
||||||
|
rkv.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rkv.KVClient.DeleteRange(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rkv *retryKVClient) Txn(ctx context.Context, in *pb.TxnRequest, opts ...grpc.CallOption) (resp *pb.TxnResponse, err error) {
|
||||||
|
rkv.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rkv.KVClient.Txn(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rkv *retryKVClient) Compact(ctx context.Context, in *pb.CompactionRequest, opts ...grpc.CallOption) (resp *pb.CompactionResponse, err error) {
|
||||||
|
rkv.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rkv.KVClient.Compact(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type retryLeaseClient struct {
|
||||||
|
pb.LeaseClient
|
||||||
|
retryf retryRpcFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryLeaseClient implements a LeaseClient that uses the client's FailFast retry policy.
|
||||||
|
func RetryLeaseClient(c *Client) pb.LeaseClient {
|
||||||
|
return &retryLeaseClient{pb.NewLeaseClient(c.conn), c.retryWrapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rlc *retryLeaseClient) LeaseGrant(ctx context.Context, in *pb.LeaseGrantRequest, opts ...grpc.CallOption) (resp *pb.LeaseGrantResponse, err error) {
|
||||||
|
rlc.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rlc.LeaseClient.LeaseGrant(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rlc *retryLeaseClient) LeaseRevoke(ctx context.Context, in *pb.LeaseRevokeRequest, opts ...grpc.CallOption) (resp *pb.LeaseRevokeResponse, err error) {
|
||||||
|
rlc.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rlc.LeaseClient.LeaseRevoke(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type retryClusterClient struct {
|
||||||
|
pb.ClusterClient
|
||||||
|
retryf retryRpcFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryClusterClient implements a ClusterClient that uses the client's FailFast retry policy.
|
||||||
|
func RetryClusterClient(c *Client) pb.ClusterClient {
|
||||||
|
return &retryClusterClient{pb.NewClusterClient(c.conn), c.retryWrapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rcc *retryClusterClient) MemberAdd(ctx context.Context, in *pb.MemberAddRequest, opts ...grpc.CallOption) (resp *pb.MemberAddResponse, err error) {
|
||||||
|
rcc.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rcc.ClusterClient.MemberAdd(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rcc *retryClusterClient) MemberRemove(ctx context.Context, in *pb.MemberRemoveRequest, opts ...grpc.CallOption) (resp *pb.MemberRemoveResponse, err error) {
|
||||||
|
rcc.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rcc.ClusterClient.MemberRemove(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rcc *retryClusterClient) MemberUpdate(ctx context.Context, in *pb.MemberUpdateRequest, opts ...grpc.CallOption) (resp *pb.MemberUpdateResponse, err error) {
|
||||||
|
rcc.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rcc.ClusterClient.MemberUpdate(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type retryAuthClient struct {
|
||||||
|
pb.AuthClient
|
||||||
|
retryf retryRpcFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryAuthClient implements a AuthClient that uses the client's FailFast retry policy.
|
||||||
|
func RetryAuthClient(c *Client) pb.AuthClient {
|
||||||
|
return &retryAuthClient{pb.NewAuthClient(c.conn), c.retryWrapper}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) AuthEnable(ctx context.Context, in *pb.AuthEnableRequest, opts ...grpc.CallOption) (resp *pb.AuthEnableResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.AuthEnable(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) AuthDisable(ctx context.Context, in *pb.AuthDisableRequest, opts ...grpc.CallOption) (resp *pb.AuthDisableResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.AuthDisable(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) UserAdd(ctx context.Context, in *pb.AuthUserAddRequest, opts ...grpc.CallOption) (resp *pb.AuthUserAddResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.UserAdd(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) UserDelete(ctx context.Context, in *pb.AuthUserDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthUserDeleteResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.UserDelete(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) UserChangePassword(ctx context.Context, in *pb.AuthUserChangePasswordRequest, opts ...grpc.CallOption) (resp *pb.AuthUserChangePasswordResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.UserChangePassword(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) UserGrantRole(ctx context.Context, in *pb.AuthUserGrantRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserGrantRoleResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.UserGrantRole(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) UserRevokeRole(ctx context.Context, in *pb.AuthUserRevokeRoleRequest, opts ...grpc.CallOption) (resp *pb.AuthUserRevokeRoleResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.UserRevokeRole(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) RoleAdd(ctx context.Context, in *pb.AuthRoleAddRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleAddResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.RoleAdd(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) RoleDelete(ctx context.Context, in *pb.AuthRoleDeleteRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleDeleteResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.RoleDelete(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) RoleGrantPermission(ctx context.Context, in *pb.AuthRoleGrantPermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleGrantPermissionResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.RoleGrantPermission(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rac *retryAuthClient) RoleRevokePermission(ctx context.Context, in *pb.AuthRoleRevokePermissionRequest, opts ...grpc.CallOption) (resp *pb.AuthRoleRevokePermissionResponse, err error) {
|
||||||
|
rac.retryf(ctx, func(rctx context.Context) error {
|
||||||
|
resp, err = rac.AuthClient.RoleRevokePermission(rctx, in, opts...)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return resp, err
|
||||||
|
}
|
37
vendor/github.com/coreos/etcd/clientv3/sort.go
generated
vendored
Normal file
37
vendor/github.com/coreos/etcd/clientv3/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
|
||||||
|
}
|
160
vendor/github.com/coreos/etcd/clientv3/txn.go
generated
vendored
Normal file
160
vendor/github.com/coreos/etcd/clientv3/txn.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
// 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 (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Txn is the interface that wraps mini-transactions.
|
||||||
|
//
|
||||||
|
// Tx.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)
|
||||||
|
|
||||||
|
// TODO: add a Do for shortcut the txn without any condition?
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
for {
|
||||||
|
resp, err := txn.commit()
|
||||||
|
if err == nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
if isHaltErr(txn.ctx, err) {
|
||||||
|
return nil, toErr(txn.ctx, err)
|
||||||
|
}
|
||||||
|
if txn.isWrite {
|
||||||
|
return nil, toErr(txn.ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (txn *txn) commit() (*TxnResponse, error) {
|
||||||
|
r := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
|
||||||
|
resp, err := txn.kv.remote.Txn(txn.ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*TxnResponse)(resp), nil
|
||||||
|
}
|
714
vendor/github.com/coreos/etcd/clientv3/watch.go
generated
vendored
Normal file
714
vendor/github.com/coreos/etcd/clientv3/watch.go
generated
vendored
Normal file
|
@ -0,0 +1,714 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v3rpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||||
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
|
mvccpb "github.com/coreos/etcd/mvcc/mvccpb"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventTypeDelete = mvccpb.DELETE
|
||||||
|
EventTypePut = mvccpb.PUT
|
||||||
|
|
||||||
|
closeSendErrTimeout = 250 * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
|
type Event mvccpb.Event
|
||||||
|
|
||||||
|
type WatchChan <-chan WatchResponse
|
||||||
|
|
||||||
|
type Watcher interface {
|
||||||
|
// Watch watches on a key or prefix. The watched events will be returned
|
||||||
|
// through the returned channel.
|
||||||
|
// If the watch is slow or the required rev is compacted, the watch request
|
||||||
|
// might be canceled from the server-side and the chan will be closed.
|
||||||
|
// 'opts' can be: 'WithRev' and/or 'WithPrefix'.
|
||||||
|
Watch(ctx context.Context, key string, opts ...OpOption) WatchChan
|
||||||
|
|
||||||
|
// Close closes the watcher and cancels all watch requests.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type WatchResponse struct {
|
||||||
|
Header pb.ResponseHeader
|
||||||
|
Events []*Event
|
||||||
|
|
||||||
|
// CompactRevision is the minimum revision the watcher may receive.
|
||||||
|
CompactRevision int64
|
||||||
|
|
||||||
|
// Canceled is used to indicate watch failure.
|
||||||
|
// If the watch failed and the stream was about to close, before the channel is closed,
|
||||||
|
// the channel sends a final response that has Canceled set to true with a non-nil Err().
|
||||||
|
Canceled bool
|
||||||
|
|
||||||
|
closeErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsCreate returns true if the event tells that the key is newly created.
|
||||||
|
func (e *Event) IsCreate() bool {
|
||||||
|
return e.Type == EventTypePut && e.Kv.CreateRevision == e.Kv.ModRevision
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsModify returns true if the event tells that a new value is put on existing key.
|
||||||
|
func (e *Event) IsModify() bool {
|
||||||
|
return e.Type == EventTypePut && e.Kv.CreateRevision != e.Kv.ModRevision
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err is the error value if this WatchResponse holds an error.
|
||||||
|
func (wr *WatchResponse) Err() error {
|
||||||
|
switch {
|
||||||
|
case wr.closeErr != nil:
|
||||||
|
return v3rpc.Error(wr.closeErr)
|
||||||
|
case wr.CompactRevision != 0:
|
||||||
|
return v3rpc.ErrCompacted
|
||||||
|
case wr.Canceled:
|
||||||
|
return v3rpc.ErrFutureRev
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProgressNotify returns true if the WatchResponse is progress notification.
|
||||||
|
func (wr *WatchResponse) IsProgressNotify() bool {
|
||||||
|
return len(wr.Events) == 0 && !wr.Canceled
|
||||||
|
}
|
||||||
|
|
||||||
|
// watcher implements the Watcher interface
|
||||||
|
type watcher struct {
|
||||||
|
remote pb.WatchClient
|
||||||
|
|
||||||
|
// mu protects the grpc streams map
|
||||||
|
mu sync.RWMutex
|
||||||
|
// streams holds all the active grpc streams keyed by ctx value.
|
||||||
|
streams map[string]*watchGrpcStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type watchGrpcStream struct {
|
||||||
|
owner *watcher
|
||||||
|
remote pb.WatchClient
|
||||||
|
|
||||||
|
// ctx controls internal remote.Watch requests
|
||||||
|
ctx context.Context
|
||||||
|
// ctxKey is the key used when looking up this stream's context
|
||||||
|
ctxKey string
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// mu protects the streams map
|
||||||
|
mu sync.RWMutex
|
||||||
|
// streams holds all active watchers
|
||||||
|
streams map[int64]*watcherStream
|
||||||
|
|
||||||
|
// reqc sends a watch request from Watch() to the main goroutine
|
||||||
|
reqc chan *watchRequest
|
||||||
|
// respc receives data from the watch client
|
||||||
|
respc chan *pb.WatchResponse
|
||||||
|
// stopc is sent to the main goroutine to stop all processing
|
||||||
|
stopc chan struct{}
|
||||||
|
// donec closes to broadcast shutdown
|
||||||
|
donec chan struct{}
|
||||||
|
// errc transmits errors from grpc Recv to the watch stream reconn logic
|
||||||
|
errc chan error
|
||||||
|
|
||||||
|
// the error that closed the watch stream
|
||||||
|
closeErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// watchRequest is issued by the subscriber to start a new watcher
|
||||||
|
type watchRequest struct {
|
||||||
|
ctx context.Context
|
||||||
|
key string
|
||||||
|
end string
|
||||||
|
rev int64
|
||||||
|
// progressNotify is for progress updates.
|
||||||
|
progressNotify bool
|
||||||
|
// retc receives a chan WatchResponse once the watcher is established
|
||||||
|
retc chan chan WatchResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// watcherStream represents a registered watcher
|
||||||
|
type watcherStream struct {
|
||||||
|
// initReq is the request that initiated this request
|
||||||
|
initReq watchRequest
|
||||||
|
|
||||||
|
// outc publishes watch responses to subscriber
|
||||||
|
outc chan<- WatchResponse
|
||||||
|
// recvc buffers watch responses before publishing
|
||||||
|
recvc chan *WatchResponse
|
||||||
|
id int64
|
||||||
|
|
||||||
|
// lastRev is revision last successfully sent over outc
|
||||||
|
lastRev int64
|
||||||
|
// resumec indicates the stream must recover at a given revision
|
||||||
|
resumec chan int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWatcher(c *Client) Watcher {
|
||||||
|
return &watcher{
|
||||||
|
remote: pb.NewWatchClient(c.conn),
|
||||||
|
streams: make(map[string]*watchGrpcStream),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// never closes
|
||||||
|
var valCtxCh = make(chan struct{})
|
||||||
|
var zeroTime = time.Unix(0, 0)
|
||||||
|
|
||||||
|
// ctx with only the values; never Done
|
||||||
|
type valCtx struct{ context.Context }
|
||||||
|
|
||||||
|
func (vc *valCtx) Deadline() (time.Time, bool) { return zeroTime, false }
|
||||||
|
func (vc *valCtx) Done() <-chan struct{} { return valCtxCh }
|
||||||
|
func (vc *valCtx) Err() error { return nil }
|
||||||
|
|
||||||
|
func (w *watcher) newWatcherGrpcStream(inctx context.Context) *watchGrpcStream {
|
||||||
|
ctx, cancel := context.WithCancel(&valCtx{inctx})
|
||||||
|
wgs := &watchGrpcStream{
|
||||||
|
owner: w,
|
||||||
|
remote: w.remote,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxKey: fmt.Sprintf("%v", inctx),
|
||||||
|
cancel: cancel,
|
||||||
|
streams: make(map[int64]*watcherStream),
|
||||||
|
|
||||||
|
respc: make(chan *pb.WatchResponse),
|
||||||
|
reqc: make(chan *watchRequest),
|
||||||
|
stopc: make(chan struct{}),
|
||||||
|
donec: make(chan struct{}),
|
||||||
|
errc: make(chan error, 1),
|
||||||
|
}
|
||||||
|
go wgs.run()
|
||||||
|
return wgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch posts a watch request to run() and waits for a new watcher channel
|
||||||
|
func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) WatchChan {
|
||||||
|
ow := opWatch(key, opts...)
|
||||||
|
|
||||||
|
retc := make(chan chan WatchResponse, 1)
|
||||||
|
wr := &watchRequest{
|
||||||
|
ctx: ctx,
|
||||||
|
key: string(ow.key),
|
||||||
|
end: string(ow.end),
|
||||||
|
rev: ow.rev,
|
||||||
|
progressNotify: ow.progressNotify,
|
||||||
|
retc: retc,
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
ctxKey := fmt.Sprintf("%v", ctx)
|
||||||
|
|
||||||
|
// find or allocate appropriate grpc watch stream
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.streams == nil {
|
||||||
|
// closed
|
||||||
|
w.mu.Unlock()
|
||||||
|
ch := make(chan WatchResponse)
|
||||||
|
close(ch)
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
wgs := w.streams[ctxKey]
|
||||||
|
if wgs == nil {
|
||||||
|
wgs = w.newWatcherGrpcStream(ctx)
|
||||||
|
w.streams[ctxKey] = wgs
|
||||||
|
}
|
||||||
|
donec := wgs.donec
|
||||||
|
reqc := wgs.reqc
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// couldn't create channel; return closed channel
|
||||||
|
closeCh := make(chan WatchResponse, 1)
|
||||||
|
|
||||||
|
// submit request
|
||||||
|
select {
|
||||||
|
case reqc <- wr:
|
||||||
|
ok = true
|
||||||
|
case <-wr.ctx.Done():
|
||||||
|
wgs.stopIfEmpty()
|
||||||
|
case <-donec:
|
||||||
|
if wgs.closeErr != nil {
|
||||||
|
closeCh <- WatchResponse{closeErr: wgs.closeErr}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// retry; may have dropped stream from no ctxs
|
||||||
|
return w.Watch(ctx, key, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive channel
|
||||||
|
if ok {
|
||||||
|
select {
|
||||||
|
case ret := <-retc:
|
||||||
|
return ret
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-donec:
|
||||||
|
if wgs.closeErr != nil {
|
||||||
|
closeCh <- WatchResponse{closeErr: wgs.closeErr}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// retry; may have dropped stream from no ctxs
|
||||||
|
return w.Watch(ctx, key, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(closeCh)
|
||||||
|
return closeCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watcher) Close() (err error) {
|
||||||
|
w.mu.Lock()
|
||||||
|
streams := w.streams
|
||||||
|
w.streams = nil
|
||||||
|
w.mu.Unlock()
|
||||||
|
for _, wgs := range streams {
|
||||||
|
if werr := wgs.Close(); werr != nil {
|
||||||
|
err = werr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watchGrpcStream) Close() (err error) {
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.stopc != nil {
|
||||||
|
close(w.stopc)
|
||||||
|
w.stopc = nil
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
<-w.donec
|
||||||
|
select {
|
||||||
|
case err = <-w.errc:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return toErr(w.ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watchGrpcStream) addStream(resp *pb.WatchResponse, pendingReq *watchRequest) {
|
||||||
|
if pendingReq == nil {
|
||||||
|
// no pending request; ignore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Canceled || resp.CompactRevision != 0 {
|
||||||
|
// a cancel at id creation time means the start revision has
|
||||||
|
// been compacted out of the store
|
||||||
|
ret := make(chan WatchResponse, 1)
|
||||||
|
ret <- WatchResponse{
|
||||||
|
Header: *resp.Header,
|
||||||
|
CompactRevision: resp.CompactRevision,
|
||||||
|
Canceled: true}
|
||||||
|
close(ret)
|
||||||
|
pendingReq.retc <- ret
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make(chan WatchResponse)
|
||||||
|
if resp.WatchId == -1 {
|
||||||
|
// failed; no channel
|
||||||
|
close(ret)
|
||||||
|
pendingReq.retc <- ret
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ws := &watcherStream{
|
||||||
|
initReq: *pendingReq,
|
||||||
|
id: resp.WatchId,
|
||||||
|
outc: ret,
|
||||||
|
// buffered so unlikely to block on sending while holding mu
|
||||||
|
recvc: make(chan *WatchResponse, 4),
|
||||||
|
resumec: make(chan int64),
|
||||||
|
}
|
||||||
|
|
||||||
|
if pendingReq.rev == 0 {
|
||||||
|
// note the header revision so that a put following a current watcher
|
||||||
|
// disconnect will arrive on the watcher channel after reconnect
|
||||||
|
ws.initReq.rev = resp.Header.Revision
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
w.streams[ws.id] = ws
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// pass back the subscriber channel for the watcher
|
||||||
|
pendingReq.retc <- ret
|
||||||
|
|
||||||
|
// send messages to subscriber
|
||||||
|
go w.serveStream(ws)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeStream closes the watcher resources and removes it
|
||||||
|
func (w *watchGrpcStream) closeStream(ws *watcherStream) {
|
||||||
|
w.mu.Lock()
|
||||||
|
// cancels request stream; subscriber receives nil channel
|
||||||
|
close(ws.initReq.retc)
|
||||||
|
// close subscriber's channel
|
||||||
|
close(ws.outc)
|
||||||
|
delete(w.streams, ws.id)
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// run is the root of the goroutines for managing a watcher client
|
||||||
|
func (w *watchGrpcStream) run() {
|
||||||
|
var wc pb.Watch_WatchClient
|
||||||
|
var closeErr error
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
w.owner.mu.Lock()
|
||||||
|
w.closeErr = closeErr
|
||||||
|
if w.owner.streams != nil {
|
||||||
|
delete(w.owner.streams, w.ctxKey)
|
||||||
|
}
|
||||||
|
close(w.donec)
|
||||||
|
w.owner.mu.Unlock()
|
||||||
|
w.cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// already stopped?
|
||||||
|
w.mu.RLock()
|
||||||
|
stopc := w.stopc
|
||||||
|
w.mu.RUnlock()
|
||||||
|
if stopc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// start a stream with the etcd grpc server
|
||||||
|
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var pendingReq, failedReq *watchRequest
|
||||||
|
curReqC := w.reqc
|
||||||
|
cancelSet := make(map[int64]struct{})
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// Watch() requested
|
||||||
|
case pendingReq = <-curReqC:
|
||||||
|
// no more watch requests until there's a response
|
||||||
|
curReqC = nil
|
||||||
|
if err := wc.Send(pendingReq.toPB()); err == nil {
|
||||||
|
// pendingReq now waits on w.respc
|
||||||
|
break
|
||||||
|
}
|
||||||
|
failedReq = pendingReq
|
||||||
|
// New events from the watch client
|
||||||
|
case pbresp := <-w.respc:
|
||||||
|
switch {
|
||||||
|
case pbresp.Created:
|
||||||
|
// response to pending req, try to add
|
||||||
|
w.addStream(pbresp, pendingReq)
|
||||||
|
pendingReq = nil
|
||||||
|
curReqC = w.reqc
|
||||||
|
case pbresp.Canceled:
|
||||||
|
delete(cancelSet, pbresp.WatchId)
|
||||||
|
// shutdown serveStream, if any
|
||||||
|
w.mu.Lock()
|
||||||
|
if ws, ok := w.streams[pbresp.WatchId]; ok {
|
||||||
|
close(ws.recvc)
|
||||||
|
delete(w.streams, ws.id)
|
||||||
|
}
|
||||||
|
numStreams := len(w.streams)
|
||||||
|
w.mu.Unlock()
|
||||||
|
if numStreams == 0 {
|
||||||
|
// don't leak watcher streams
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// dispatch to appropriate watch stream
|
||||||
|
if ok := w.dispatchEvent(pbresp); ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// watch response on unexpected watch id; cancel id
|
||||||
|
if _, ok := cancelSet[pbresp.WatchId]; ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cancelSet[pbresp.WatchId] = struct{}{}
|
||||||
|
cr := &pb.WatchRequest_CancelRequest{
|
||||||
|
CancelRequest: &pb.WatchCancelRequest{
|
||||||
|
WatchId: pbresp.WatchId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
req := &pb.WatchRequest{RequestUnion: cr}
|
||||||
|
wc.Send(req)
|
||||||
|
}
|
||||||
|
// watch client failed to recv; spawn another if possible
|
||||||
|
// TODO report watch client errors from errc?
|
||||||
|
case err := <-w.errc:
|
||||||
|
if toErr(w.ctx, err) == v3rpc.ErrNoLeader {
|
||||||
|
closeErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
curReqC = w.reqc
|
||||||
|
if pendingReq != nil {
|
||||||
|
failedReq = pendingReq
|
||||||
|
}
|
||||||
|
cancelSet = make(map[int64]struct{})
|
||||||
|
case <-stopc:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// send failed; queue for retry
|
||||||
|
if failedReq != nil {
|
||||||
|
go func(wr *watchRequest) {
|
||||||
|
select {
|
||||||
|
case w.reqc <- wr:
|
||||||
|
case <-wr.ctx.Done():
|
||||||
|
case <-w.donec:
|
||||||
|
}
|
||||||
|
}(pendingReq)
|
||||||
|
failedReq = nil
|
||||||
|
pendingReq = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatchEvent sends a WatchResponse to the appropriate watcher stream
|
||||||
|
func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
|
||||||
|
w.mu.RLock()
|
||||||
|
defer w.mu.RUnlock()
|
||||||
|
ws, ok := w.streams[pbresp.WatchId]
|
||||||
|
events := make([]*Event, len(pbresp.Events))
|
||||||
|
for i, ev := range pbresp.Events {
|
||||||
|
events[i] = (*Event)(ev)
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
wr := &WatchResponse{
|
||||||
|
Header: *pbresp.Header,
|
||||||
|
Events: events,
|
||||||
|
CompactRevision: pbresp.CompactRevision,
|
||||||
|
Canceled: pbresp.Canceled}
|
||||||
|
ws.recvc <- wr
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveWatchClient forwards messages from the grpc stream to run()
|
||||||
|
func (w *watchGrpcStream) serveWatchClient(wc pb.Watch_WatchClient) {
|
||||||
|
for {
|
||||||
|
resp, err := wc.Recv()
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case w.errc <- err:
|
||||||
|
case <-w.donec:
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case w.respc <- resp:
|
||||||
|
case <-w.donec:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveStream forwards watch responses from run() to the subscriber
|
||||||
|
func (w *watchGrpcStream) serveStream(ws *watcherStream) {
|
||||||
|
var closeErr error
|
||||||
|
emptyWr := &WatchResponse{}
|
||||||
|
wrs := []*WatchResponse{}
|
||||||
|
resuming := false
|
||||||
|
closing := false
|
||||||
|
for !closing {
|
||||||
|
curWr := emptyWr
|
||||||
|
outc := ws.outc
|
||||||
|
if len(wrs) > 0 {
|
||||||
|
curWr = wrs[0]
|
||||||
|
} else {
|
||||||
|
outc = nil
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case outc <- *curWr:
|
||||||
|
if wrs[0].Err() != nil {
|
||||||
|
closing = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var newRev int64
|
||||||
|
if len(wrs[0].Events) > 0 {
|
||||||
|
newRev = wrs[0].Events[len(wrs[0].Events)-1].Kv.ModRevision
|
||||||
|
} else {
|
||||||
|
newRev = wrs[0].Header.Revision
|
||||||
|
}
|
||||||
|
if newRev != ws.lastRev {
|
||||||
|
ws.lastRev = newRev
|
||||||
|
}
|
||||||
|
wrs[0] = nil
|
||||||
|
wrs = wrs[1:]
|
||||||
|
case wr, ok := <-ws.recvc:
|
||||||
|
if !ok {
|
||||||
|
// shutdown from closeStream
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// resume up to last seen event if disconnected
|
||||||
|
if resuming && wr.Err() == nil {
|
||||||
|
resuming = false
|
||||||
|
// trim events already seen
|
||||||
|
for i := 0; i < len(wr.Events); i++ {
|
||||||
|
if wr.Events[i].Kv.ModRevision > ws.lastRev {
|
||||||
|
wr.Events = wr.Events[i:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// only forward new events
|
||||||
|
if wr.Events[0].Kv.ModRevision == ws.lastRev {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resuming = false
|
||||||
|
// TODO don't keep buffering if subscriber stops reading
|
||||||
|
wrs = append(wrs, wr)
|
||||||
|
case resumeRev := <-ws.resumec:
|
||||||
|
wrs = nil
|
||||||
|
resuming = true
|
||||||
|
if resumeRev == -1 {
|
||||||
|
// pause serving stream while resume gets set up
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if resumeRev != ws.lastRev {
|
||||||
|
panic("unexpected resume revision")
|
||||||
|
}
|
||||||
|
case <-w.donec:
|
||||||
|
closing = true
|
||||||
|
closeErr = w.closeErr
|
||||||
|
case <-ws.initReq.ctx.Done():
|
||||||
|
closing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to send off close error
|
||||||
|
if closeErr != nil {
|
||||||
|
select {
|
||||||
|
case ws.outc <- WatchResponse{closeErr: w.closeErr}:
|
||||||
|
case <-w.donec:
|
||||||
|
case <-time.After(closeSendErrTimeout):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.closeStream(ws)
|
||||||
|
w.stopIfEmpty()
|
||||||
|
// lazily send cancel message if events on missing id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wgs *watchGrpcStream) stopIfEmpty() {
|
||||||
|
wgs.mu.Lock()
|
||||||
|
if len(wgs.streams) == 0 && wgs.stopc != nil {
|
||||||
|
close(wgs.stopc)
|
||||||
|
wgs.stopc = nil
|
||||||
|
}
|
||||||
|
wgs.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *watchGrpcStream) newWatchClient() (pb.Watch_WatchClient, error) {
|
||||||
|
ws, rerr := w.resume()
|
||||||
|
if rerr != nil {
|
||||||
|
return nil, rerr
|
||||||
|
}
|
||||||
|
go w.serveWatchClient(ws)
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume creates a new WatchClient with all current watchers reestablished
|
||||||
|
func (w *watchGrpcStream) resume() (ws pb.Watch_WatchClient, err error) {
|
||||||
|
for {
|
||||||
|
if ws, err = w.openWatchClient(); err != nil {
|
||||||
|
break
|
||||||
|
} else if err = w.resumeWatchers(ws); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ws, v3rpc.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// openWatchClient retries opening a watchclient until retryConnection fails
|
||||||
|
func (w *watchGrpcStream) openWatchClient() (ws pb.Watch_WatchClient, err error) {
|
||||||
|
for {
|
||||||
|
w.mu.Lock()
|
||||||
|
stopc := w.stopc
|
||||||
|
w.mu.Unlock()
|
||||||
|
if stopc == nil {
|
||||||
|
if err == nil {
|
||||||
|
err = context.Canceled
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ws, err = w.remote.Watch(w.ctx, grpc.FailFast(false)); ws != nil && err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if isHaltErr(w.ctx, err) {
|
||||||
|
return nil, v3rpc.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resumeWatchers rebuilds every registered watcher on a new client
|
||||||
|
func (w *watchGrpcStream) resumeWatchers(wc pb.Watch_WatchClient) error {
|
||||||
|
w.mu.RLock()
|
||||||
|
streams := make([]*watcherStream, 0, len(w.streams))
|
||||||
|
for _, ws := range w.streams {
|
||||||
|
streams = append(streams, ws)
|
||||||
|
}
|
||||||
|
w.mu.RUnlock()
|
||||||
|
|
||||||
|
for _, ws := range streams {
|
||||||
|
// pause serveStream
|
||||||
|
ws.resumec <- -1
|
||||||
|
|
||||||
|
// reconstruct watcher from initial request
|
||||||
|
if ws.lastRev != 0 {
|
||||||
|
ws.initReq.rev = ws.lastRev
|
||||||
|
}
|
||||||
|
if err := wc.Send(ws.initReq.toPB()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for request ack
|
||||||
|
resp, err := wc.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(resp.Events) != 0 || !resp.Created {
|
||||||
|
return fmt.Errorf("watcher: unexpected response (%+v)", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// id may be different since new remote watcher; update map
|
||||||
|
w.mu.Lock()
|
||||||
|
delete(w.streams, ws.id)
|
||||||
|
ws.id = resp.WatchId
|
||||||
|
w.streams[ws.id] = ws
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// unpause serveStream
|
||||||
|
ws.resumec <- ws.lastRev
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toPB converts an internal watch request structure to its protobuf messagefunc (wr *watchRequest)
|
||||||
|
func (wr *watchRequest) toPB() *pb.WatchRequest {
|
||||||
|
req := &pb.WatchCreateRequest{
|
||||||
|
StartRevision: wr.rev,
|
||||||
|
Key: []byte(wr.key),
|
||||||
|
RangeEnd: []byte(wr.end),
|
||||||
|
ProgressNotify: wr.progressNotify,
|
||||||
|
}
|
||||||
|
cr := &pb.WatchRequest_CreateRequest{CreateRequest: req}
|
||||||
|
return &pb.WatchRequest{RequestUnion: cr}
|
||||||
|
}
|
16
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/doc.go
generated
vendored
Normal file
16
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/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 rpctypes has types and values shared by the etcd server and client for v3 RPC interaction.
|
||||||
|
package rpctypes
|
150
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/error.go
generated
vendored
Normal file
150
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/error.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
// 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 rpctypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// server-side error
|
||||||
|
ErrGRPCEmptyKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: key is not provided")
|
||||||
|
ErrGRPCTooManyOps = grpc.Errorf(codes.InvalidArgument, "etcdserver: too many operations in txn request")
|
||||||
|
ErrGRPCDuplicateKey = grpc.Errorf(codes.InvalidArgument, "etcdserver: duplicate key given in txn request")
|
||||||
|
ErrGRPCCompacted = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision has been compacted")
|
||||||
|
ErrGRPCFutureRev = grpc.Errorf(codes.OutOfRange, "etcdserver: mvcc: required revision is a future revision")
|
||||||
|
ErrGRPCNoSpace = grpc.Errorf(codes.ResourceExhausted, "etcdserver: mvcc: database space exceeded")
|
||||||
|
|
||||||
|
ErrGRPCLeaseNotFound = grpc.Errorf(codes.NotFound, "etcdserver: requested lease not found")
|
||||||
|
ErrGRPCLeaseExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: lease already exists")
|
||||||
|
|
||||||
|
ErrGRPCMemberExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: member ID already exist")
|
||||||
|
ErrGRPCPeerURLExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: Peer URLs already exists")
|
||||||
|
ErrGRPCMemberBadURLs = grpc.Errorf(codes.InvalidArgument, "etcdserver: given member URLs are invalid")
|
||||||
|
ErrGRPCMemberNotFound = grpc.Errorf(codes.NotFound, "etcdserver: member not found")
|
||||||
|
|
||||||
|
ErrGRPCRequestTooLarge = grpc.Errorf(codes.InvalidArgument, "etcdserver: request is too large")
|
||||||
|
|
||||||
|
ErrGRPCRootUserNotExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not exist")
|
||||||
|
ErrGRPCRootRoleNotExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: root user does not have root role")
|
||||||
|
ErrGRPCUserAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name already exists")
|
||||||
|
ErrGRPCUserNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: user name not found")
|
||||||
|
ErrGRPCRoleAlreadyExist = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name already exists")
|
||||||
|
ErrGRPCRoleNotFound = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role name not found")
|
||||||
|
ErrGRPCAuthFailed = grpc.Errorf(codes.InvalidArgument, "etcdserver: authentication failed, invalid user ID or password")
|
||||||
|
ErrGRPCPermissionDenied = grpc.Errorf(codes.FailedPrecondition, "etcdserver: permission denied")
|
||||||
|
ErrGRPCRoleNotGranted = grpc.Errorf(codes.FailedPrecondition, "etcdserver: role is not granted to the user")
|
||||||
|
ErrGRPCPermissionNotGranted = grpc.Errorf(codes.FailedPrecondition, "etcdserver: permission is not granted to the role")
|
||||||
|
|
||||||
|
ErrGRPCNoLeader = grpc.Errorf(codes.Unavailable, "etcdserver: no leader")
|
||||||
|
ErrGRPCNotCapable = grpc.Errorf(codes.Unavailable, "etcdserver: not capable")
|
||||||
|
ErrGRPCStopped = grpc.Errorf(codes.Unavailable, "etcdserver: server stopped")
|
||||||
|
|
||||||
|
errStringToError = map[string]error{
|
||||||
|
grpc.ErrorDesc(ErrGRPCEmptyKey): ErrGRPCEmptyKey,
|
||||||
|
grpc.ErrorDesc(ErrGRPCTooManyOps): ErrGRPCTooManyOps,
|
||||||
|
grpc.ErrorDesc(ErrGRPCDuplicateKey): ErrGRPCDuplicateKey,
|
||||||
|
grpc.ErrorDesc(ErrGRPCCompacted): ErrGRPCCompacted,
|
||||||
|
grpc.ErrorDesc(ErrGRPCFutureRev): ErrGRPCFutureRev,
|
||||||
|
grpc.ErrorDesc(ErrGRPCNoSpace): ErrGRPCNoSpace,
|
||||||
|
|
||||||
|
grpc.ErrorDesc(ErrGRPCLeaseNotFound): ErrGRPCLeaseNotFound,
|
||||||
|
grpc.ErrorDesc(ErrGRPCLeaseExist): ErrGRPCLeaseExist,
|
||||||
|
|
||||||
|
grpc.ErrorDesc(ErrGRPCMemberExist): ErrGRPCMemberExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCPeerURLExist): ErrGRPCPeerURLExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCMemberBadURLs): ErrGRPCMemberBadURLs,
|
||||||
|
grpc.ErrorDesc(ErrGRPCMemberNotFound): ErrGRPCMemberNotFound,
|
||||||
|
|
||||||
|
grpc.ErrorDesc(ErrGRPCRequestTooLarge): ErrGRPCRequestTooLarge,
|
||||||
|
|
||||||
|
grpc.ErrorDesc(ErrGRPCRootUserNotExist): ErrGRPCRootUserNotExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCRootRoleNotExist): ErrGRPCRootRoleNotExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCUserAlreadyExist): ErrGRPCUserAlreadyExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCUserNotFound): ErrGRPCUserNotFound,
|
||||||
|
grpc.ErrorDesc(ErrGRPCRoleAlreadyExist): ErrGRPCRoleAlreadyExist,
|
||||||
|
grpc.ErrorDesc(ErrGRPCRoleNotFound): ErrGRPCRoleNotFound,
|
||||||
|
grpc.ErrorDesc(ErrGRPCAuthFailed): ErrGRPCAuthFailed,
|
||||||
|
grpc.ErrorDesc(ErrGRPCPermissionDenied): ErrGRPCPermissionDenied,
|
||||||
|
grpc.ErrorDesc(ErrGRPCRoleNotGranted): ErrGRPCRoleNotGranted,
|
||||||
|
grpc.ErrorDesc(ErrGRPCPermissionNotGranted): ErrGRPCPermissionNotGranted,
|
||||||
|
|
||||||
|
grpc.ErrorDesc(ErrGRPCNoLeader): ErrGRPCNoLeader,
|
||||||
|
grpc.ErrorDesc(ErrGRPCNotCapable): ErrGRPCNotCapable,
|
||||||
|
grpc.ErrorDesc(ErrGRPCStopped): ErrGRPCStopped,
|
||||||
|
}
|
||||||
|
|
||||||
|
// client-side error
|
||||||
|
ErrEmptyKey = Error(ErrGRPCEmptyKey)
|
||||||
|
ErrTooManyOps = Error(ErrGRPCTooManyOps)
|
||||||
|
ErrDuplicateKey = Error(ErrGRPCDuplicateKey)
|
||||||
|
ErrCompacted = Error(ErrGRPCCompacted)
|
||||||
|
ErrFutureRev = Error(ErrGRPCFutureRev)
|
||||||
|
ErrNoSpace = Error(ErrGRPCNoSpace)
|
||||||
|
|
||||||
|
ErrLeaseNotFound = Error(ErrGRPCLeaseNotFound)
|
||||||
|
ErrLeaseExist = Error(ErrGRPCLeaseExist)
|
||||||
|
|
||||||
|
ErrMemberExist = Error(ErrGRPCMemberExist)
|
||||||
|
ErrPeerURLExist = Error(ErrGRPCPeerURLExist)
|
||||||
|
ErrMemberBadURLs = Error(ErrGRPCMemberBadURLs)
|
||||||
|
ErrMemberNotFound = Error(ErrGRPCMemberNotFound)
|
||||||
|
|
||||||
|
ErrRequestTooLarge = Error(ErrGRPCRequestTooLarge)
|
||||||
|
|
||||||
|
ErrRootUserNotExist = Error(ErrGRPCRootUserNotExist)
|
||||||
|
ErrRootRoleNotExist = Error(ErrGRPCRootRoleNotExist)
|
||||||
|
ErrUserAlreadyExist = Error(ErrGRPCUserAlreadyExist)
|
||||||
|
ErrUserNotFound = Error(ErrGRPCUserNotFound)
|
||||||
|
ErrRoleAlreadyExist = Error(ErrGRPCRoleAlreadyExist)
|
||||||
|
ErrRoleNotFound = Error(ErrGRPCRoleNotFound)
|
||||||
|
ErrAuthFailed = Error(ErrGRPCAuthFailed)
|
||||||
|
ErrPermissionDenied = Error(ErrGRPCPermissionDenied)
|
||||||
|
ErrRoleNotGranted = Error(ErrGRPCRoleNotGranted)
|
||||||
|
ErrPermissionNotGranted = Error(ErrGRPCPermissionNotGranted)
|
||||||
|
|
||||||
|
ErrNoLeader = Error(ErrGRPCNoLeader)
|
||||||
|
ErrNotCapable = Error(ErrGRPCNotCapable)
|
||||||
|
ErrStopped = Error(ErrGRPCStopped)
|
||||||
|
)
|
||||||
|
|
||||||
|
// EtcdError defines gRPC server errors.
|
||||||
|
// (https://github.com/grpc/grpc-go/blob/master/rpc_util.go#L319-L323)
|
||||||
|
type EtcdError struct {
|
||||||
|
code codes.Code
|
||||||
|
desc string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns grpc/codes.Code.
|
||||||
|
// TODO: define clientv3/codes.Code.
|
||||||
|
func (e EtcdError) Code() codes.Code {
|
||||||
|
return e.code
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e EtcdError) Error() string {
|
||||||
|
return e.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
verr, ok := errStringToError[grpc.ErrorDesc(err)]
|
||||||
|
if !ok { // not gRPC error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return EtcdError{code: grpc.Code(verr), desc: grpc.ErrorDesc(verr)}
|
||||||
|
}
|
20
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/md.go
generated
vendored
Normal file
20
vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes/md.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// 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 rpctypes
|
||||||
|
|
||||||
|
var (
|
||||||
|
MetadataRequireLeaderKey = "hasleader"
|
||||||
|
MetadataHasLeader = "true"
|
||||||
|
)
|
1041
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.pb.go
generated
vendored
Normal file
1041
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
34
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.proto
generated
vendored
Normal file
34
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.proto
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
syntax = "proto2";
|
||||||
|
package etcdserverpb;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option (gogoproto.marshaler_all) = true;
|
||||||
|
option (gogoproto.sizer_all) = true;
|
||||||
|
option (gogoproto.unmarshaler_all) = true;
|
||||||
|
option (gogoproto.goproto_getters_all) = false;
|
||||||
|
|
||||||
|
message Request {
|
||||||
|
optional uint64 ID = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional string Method = 2 [(gogoproto.nullable) = false];
|
||||||
|
optional string Path = 3 [(gogoproto.nullable) = false];
|
||||||
|
optional string Val = 4 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Dir = 5 [(gogoproto.nullable) = false];
|
||||||
|
optional string PrevValue = 6 [(gogoproto.nullable) = false];
|
||||||
|
optional uint64 PrevIndex = 7 [(gogoproto.nullable) = false];
|
||||||
|
optional bool PrevExist = 8 [(gogoproto.nullable) = true];
|
||||||
|
optional int64 Expiration = 9 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Wait = 10 [(gogoproto.nullable) = false];
|
||||||
|
optional uint64 Since = 11 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Recursive = 12 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Sorted = 13 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Quorum = 14 [(gogoproto.nullable) = false];
|
||||||
|
optional int64 Time = 15 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Stream = 16 [(gogoproto.nullable) = false];
|
||||||
|
optional bool Refresh = 17 [(gogoproto.nullable) = true];
|
||||||
|
}
|
||||||
|
|
||||||
|
message Metadata {
|
||||||
|
optional uint64 NodeID = 1 [(gogoproto.nullable) = false];
|
||||||
|
optional uint64 ClusterID = 2 [(gogoproto.nullable) = false];
|
||||||
|
}
|
2062
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.pb.go
generated
vendored
Normal file
2062
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
72
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.proto
generated
vendored
Normal file
72
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.proto
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package etcdserverpb;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
import "etcdserver.proto";
|
||||||
|
import "rpc.proto";
|
||||||
|
|
||||||
|
option (gogoproto.marshaler_all) = true;
|
||||||
|
option (gogoproto.sizer_all) = true;
|
||||||
|
option (gogoproto.unmarshaler_all) = true;
|
||||||
|
option (gogoproto.goproto_getters_all) = false;
|
||||||
|
|
||||||
|
message RequestHeader {
|
||||||
|
uint64 ID = 1;
|
||||||
|
// username is a username that is associated with an auth token of gRPC connection
|
||||||
|
string username = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An InternalRaftRequest is the union of all requests which can be
|
||||||
|
// sent via raft.
|
||||||
|
message InternalRaftRequest {
|
||||||
|
RequestHeader header = 100;
|
||||||
|
uint64 ID = 1;
|
||||||
|
|
||||||
|
Request v2 = 2;
|
||||||
|
|
||||||
|
RangeRequest range = 3;
|
||||||
|
PutRequest put = 4;
|
||||||
|
DeleteRangeRequest delete_range = 5;
|
||||||
|
TxnRequest txn = 6;
|
||||||
|
CompactionRequest compaction = 7;
|
||||||
|
|
||||||
|
LeaseGrantRequest lease_grant = 8;
|
||||||
|
LeaseRevokeRequest lease_revoke = 9;
|
||||||
|
|
||||||
|
AlarmRequest alarm = 10;
|
||||||
|
|
||||||
|
AuthEnableRequest auth_enable = 1000;
|
||||||
|
AuthDisableRequest auth_disable = 1011;
|
||||||
|
|
||||||
|
InternalAuthenticateRequest authenticate = 1012;
|
||||||
|
|
||||||
|
AuthUserAddRequest auth_user_add = 1100;
|
||||||
|
AuthUserDeleteRequest auth_user_delete = 1101;
|
||||||
|
AuthUserGetRequest auth_user_get = 1102;
|
||||||
|
AuthUserChangePasswordRequest auth_user_change_password = 1103;
|
||||||
|
AuthUserGrantRoleRequest auth_user_grant_role = 1104;
|
||||||
|
AuthUserRevokeRoleRequest auth_user_revoke_role = 1105;
|
||||||
|
AuthUserListRequest auth_user_list = 1106;
|
||||||
|
AuthRoleListRequest auth_role_list = 1107;
|
||||||
|
|
||||||
|
AuthRoleAddRequest auth_role_add = 1200;
|
||||||
|
AuthRoleDeleteRequest auth_role_delete = 1201;
|
||||||
|
AuthRoleGetRequest auth_role_get = 1202;
|
||||||
|
AuthRoleGrantPermissionRequest auth_role_grant_permission = 1203;
|
||||||
|
AuthRoleRevokePermissionRequest auth_role_revoke_permission = 1204;
|
||||||
|
}
|
||||||
|
|
||||||
|
message EmptyResponse {
|
||||||
|
}
|
||||||
|
|
||||||
|
// What is the difference between AuthenticateRequest (defined in rpc.proto) and InternalAuthenticateRequest?
|
||||||
|
// InternalAuthenticateRequest has a member that is filled by etcdserver and shouldn't be user-facing.
|
||||||
|
// For avoiding misusage the field, we have an internal version of AuthenticateRequest.
|
||||||
|
message InternalAuthenticateRequest {
|
||||||
|
string name = 1;
|
||||||
|
string password = 2;
|
||||||
|
|
||||||
|
// simple_token is generated in API layer (etcdserver/v3_server.go)
|
||||||
|
string simple_token = 3;
|
||||||
|
}
|
||||||
|
|
15327
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.go
generated
vendored
Normal file
15327
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1866
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.gw.go
generated
vendored
Normal file
1866
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.gw.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
894
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.proto
generated
vendored
Normal file
894
vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.proto
generated
vendored
Normal file
|
@ -0,0 +1,894 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package etcdserverpb;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
import "etcd/mvcc/mvccpb/kv.proto";
|
||||||
|
import "etcd/auth/authpb/auth.proto";
|
||||||
|
|
||||||
|
// for grpc-gateway
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
option (gogoproto.marshaler_all) = true;
|
||||||
|
option (gogoproto.unmarshaler_all) = true;
|
||||||
|
|
||||||
|
service KV {
|
||||||
|
// Range gets the keys in the range from the key-value store.
|
||||||
|
rpc Range(RangeRequest) returns (RangeResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/range"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put puts the given key into the key-value store.
|
||||||
|
// A put request increments the revision of the key-value store
|
||||||
|
// and generates one event in the event history.
|
||||||
|
rpc Put(PutRequest) returns (PutResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/put"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRange deletes the given range from the key-value store.
|
||||||
|
// A delete request increments the revision of the key-value store
|
||||||
|
// and generates a delete event in the event history for every deleted key.
|
||||||
|
rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/deleterange"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Txn processes multiple requests in a single transaction.
|
||||||
|
// A txn request increments the revision of the key-value store
|
||||||
|
// and generates events with the same revision for every completed request.
|
||||||
|
// It is not allowed to modify the same key several times within one txn.
|
||||||
|
rpc Txn(TxnRequest) returns (TxnResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/txn"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact compacts the event history in the etcd key-value store. The key-value
|
||||||
|
// store should be periodically compacted or the event history will continue to grow
|
||||||
|
// indefinitely.
|
||||||
|
rpc Compact(CompactionRequest) returns (CompactionResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/compaction"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service Watch {
|
||||||
|
// Watch watches for events happening or that have happened. Both input and output
|
||||||
|
// are streams; the input stream is for creating and canceling watchers and the output
|
||||||
|
// stream sends events. One watch RPC can watch on multiple key ranges, streaming events
|
||||||
|
// for several watches at once. The entire event history can be watched starting from the
|
||||||
|
// last compaction revision.
|
||||||
|
rpc Watch(stream WatchRequest) returns (stream WatchResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/watch"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service Lease {
|
||||||
|
// LeaseGrant creates a lease which expires if the server does not receive a keepAlive
|
||||||
|
// within a given time to live period. All keys attached to the lease will be expired and
|
||||||
|
// deleted if the lease expires. Each expired key generates a delete event in the event history.
|
||||||
|
rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/lease/grant"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaseRevoke revokes a lease. All keys attached to the lease will expire and be deleted.
|
||||||
|
rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/kv/lease/revoke"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaseKeepAlive keeps the lease alive by streaming keep alive requests from the client
|
||||||
|
// to the server and streaming keep alive responses from the server to the client.
|
||||||
|
rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/lease/keepalive"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(xiangli) List all existing Leases?
|
||||||
|
// TODO(xiangli) Get details information (expirations, leased keys, etc.) of a lease?
|
||||||
|
}
|
||||||
|
|
||||||
|
service Cluster {
|
||||||
|
// MemberAdd adds a member into the cluster.
|
||||||
|
rpc MemberAdd(MemberAddRequest) returns (MemberAddResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/cluster/member/add"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberRemove removes an existing member from the cluster.
|
||||||
|
rpc MemberRemove(MemberRemoveRequest) returns (MemberRemoveResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/cluster/member/remove"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberUpdate updates the member configuration.
|
||||||
|
rpc MemberUpdate(MemberUpdateRequest) returns (MemberUpdateResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/cluster/member/update"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemberList lists all the members in the cluster.
|
||||||
|
rpc MemberList(MemberListRequest) returns (MemberListResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/cluster/member/list"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service Maintenance {
|
||||||
|
// Alarm activates, deactivates, and queries alarms regarding cluster health.
|
||||||
|
rpc Alarm(AlarmRequest) returns (AlarmResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/alarm"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status gets the status of the member.
|
||||||
|
rpc Status(StatusRequest) returns (StatusResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/status"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defragment defragments a member's backend database to recover storage space.
|
||||||
|
rpc Defragment(DefragmentRequest) returns (DefragmentResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/defragment"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of the local KV state for consistency checking purpose.
|
||||||
|
// This is designed for testing; do not use this in production when there
|
||||||
|
// are ongoing transactions.
|
||||||
|
rpc Hash(HashRequest) returns (HashResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/hash"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot sends a snapshot of the entire backend from a member over a stream to a client.
|
||||||
|
rpc Snapshot(SnapshotRequest) returns (stream SnapshotResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/maintenance/snapshot"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
service Auth {
|
||||||
|
// AuthEnable enables authentication.
|
||||||
|
rpc AuthEnable(AuthEnableRequest) returns (AuthEnableResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/enable"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthDisable disables authentication.
|
||||||
|
rpc AuthDisable(AuthDisableRequest) returns (AuthDisableResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/disable"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate processes an authenticate request.
|
||||||
|
rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/authenticate"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAdd adds a new user.
|
||||||
|
rpc UserAdd(AuthUserAddRequest) returns (AuthUserAddResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/add"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserGet gets detailed user information.
|
||||||
|
rpc UserGet(AuthUserGetRequest) returns (AuthUserGetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/get"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserList gets a list of all users.
|
||||||
|
rpc UserList(AuthUserListRequest) returns (AuthUserListResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/list"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserDelete deletes a specified user.
|
||||||
|
rpc UserDelete(AuthUserDeleteRequest) returns (AuthUserDeleteResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/delete"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserChangePassword changes the password of a specified user.
|
||||||
|
rpc UserChangePassword(AuthUserChangePasswordRequest) returns (AuthUserChangePasswordResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/changepw"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserGrant grants a role to a specified user.
|
||||||
|
rpc UserGrantRole(AuthUserGrantRoleRequest) returns (AuthUserGrantRoleResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/grant"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRevokeRole revokes a role of specified user.
|
||||||
|
rpc UserRevokeRole(AuthUserRevokeRoleRequest) returns (AuthUserRevokeRoleResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/user/revoke"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleAdd adds a new role.
|
||||||
|
rpc RoleAdd(AuthRoleAddRequest) returns (AuthRoleAddResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/add"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleGet gets detailed role information.
|
||||||
|
rpc RoleGet(AuthRoleGetRequest) returns (AuthRoleGetResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/get"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleList gets lists of all roles.
|
||||||
|
rpc RoleList(AuthRoleListRequest) returns (AuthRoleListResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/list"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleDelete deletes a specified role.
|
||||||
|
rpc RoleDelete(AuthRoleDeleteRequest) returns (AuthRoleDeleteResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/delete"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleGrantPermission grants a permission of a specified key or range to a specified role.
|
||||||
|
rpc RoleGrantPermission(AuthRoleGrantPermissionRequest) returns (AuthRoleGrantPermissionResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/grant"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleRevokePermission revokes a key or range permission of a specified role.
|
||||||
|
rpc RoleRevokePermission(AuthRoleRevokePermissionRequest) returns (AuthRoleRevokePermissionResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/v3alpha/auth/role/revoke"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResponseHeader {
|
||||||
|
// cluster_id is the ID of the cluster which sent the response.
|
||||||
|
uint64 cluster_id = 1;
|
||||||
|
// member_id is the ID of the member which sent the response.
|
||||||
|
uint64 member_id = 2;
|
||||||
|
// revision is the key-value store revision when the request was applied.
|
||||||
|
int64 revision = 3;
|
||||||
|
// raft_term is the raft term when the request was applied.
|
||||||
|
uint64 raft_term = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RangeRequest {
|
||||||
|
enum SortOrder {
|
||||||
|
NONE = 0; // default, no sorting
|
||||||
|
ASCEND = 1; // lowest target value first
|
||||||
|
DESCEND = 2; // highest target value first
|
||||||
|
}
|
||||||
|
enum SortTarget {
|
||||||
|
KEY = 0;
|
||||||
|
VERSION = 1;
|
||||||
|
CREATE = 2;
|
||||||
|
MOD = 3;
|
||||||
|
VALUE = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// key is the first key for the range. If range_end is not given, the request only looks up key.
|
||||||
|
bytes key = 1;
|
||||||
|
// range_end is the upper bound on the requested range [key, range_end).
|
||||||
|
// If range_end is '\0', the range is all keys >= key.
|
||||||
|
// If the range_end is one bit larger than the given key,
|
||||||
|
// then the range requests get the all keys with the prefix (the given key).
|
||||||
|
// If both key and range_end are '\0', then range requests returns all keys.
|
||||||
|
bytes range_end = 2;
|
||||||
|
// limit is a limit on the number of keys returned for the request.
|
||||||
|
int64 limit = 3;
|
||||||
|
// revision is the point-in-time of the key-value store to use for the range.
|
||||||
|
// If revision is less or equal to zero, the range is over the newest key-value store.
|
||||||
|
// If the revision has been compacted, ErrCompacted is returned as a response.
|
||||||
|
int64 revision = 4;
|
||||||
|
|
||||||
|
// sort_order is the order for returned sorted results.
|
||||||
|
SortOrder sort_order = 5;
|
||||||
|
|
||||||
|
// sort_target is the key-value field to use for sorting.
|
||||||
|
SortTarget sort_target = 6;
|
||||||
|
|
||||||
|
// serializable sets the range request to use serializable member-local reads.
|
||||||
|
// Range requests are linearizable by default; linearizable requests have higher
|
||||||
|
// latency and lower throughput than serializable requests but reflect the current
|
||||||
|
// consensus of the cluster. For better performance, in exchange for possible stale reads,
|
||||||
|
// a serializable range request is served locally without needing to reach consensus
|
||||||
|
// with other nodes in the cluster.
|
||||||
|
bool serializable = 7;
|
||||||
|
|
||||||
|
// keys_only when set returns only the keys and not the values.
|
||||||
|
bool keys_only = 8;
|
||||||
|
|
||||||
|
// count_only when set returns only the count of the keys in the range.
|
||||||
|
bool count_only = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RangeResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// kvs is the list of key-value pairs matched by the range request.
|
||||||
|
// kvs is empty when count is requested.
|
||||||
|
repeated mvccpb.KeyValue kvs = 2;
|
||||||
|
// more indicates if there are more keys to return in the requested range.
|
||||||
|
bool more = 3;
|
||||||
|
// count is set to the number of keys within the range when requested.
|
||||||
|
int64 count = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutRequest {
|
||||||
|
// key is the key, in bytes, to put into the key-value store.
|
||||||
|
bytes key = 1;
|
||||||
|
// value is the value, in bytes, to associate with the key in the key-value store.
|
||||||
|
bytes value = 2;
|
||||||
|
// lease is the lease ID to associate with the key in the key-value store. A lease
|
||||||
|
// value of 0 indicates no lease.
|
||||||
|
int64 lease = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PutResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteRangeRequest {
|
||||||
|
// key is the first key to delete in the range.
|
||||||
|
bytes key = 1;
|
||||||
|
// range_end is the key following the last key to delete for the range [key, range_end).
|
||||||
|
// If range_end is not given, the range is defined to contain only the key argument.
|
||||||
|
// If range_end is '\0', the range is all keys greater than or equal to the key argument.
|
||||||
|
bytes range_end = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteRangeResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// deleted is the number of keys deleted by the delete range request.
|
||||||
|
int64 deleted = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestOp {
|
||||||
|
// request is a union of request types accepted by a transaction.
|
||||||
|
oneof request {
|
||||||
|
RangeRequest request_range = 1;
|
||||||
|
PutRequest request_put = 2;
|
||||||
|
DeleteRangeRequest request_delete_range = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResponseOp {
|
||||||
|
// response is a union of response types returned by a transaction.
|
||||||
|
oneof response {
|
||||||
|
RangeResponse response_range = 1;
|
||||||
|
PutResponse response_put = 2;
|
||||||
|
DeleteRangeResponse response_delete_range = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Compare {
|
||||||
|
enum CompareResult {
|
||||||
|
EQUAL = 0;
|
||||||
|
GREATER = 1;
|
||||||
|
LESS = 2;
|
||||||
|
}
|
||||||
|
enum CompareTarget {
|
||||||
|
VERSION = 0;
|
||||||
|
CREATE = 1;
|
||||||
|
MOD = 2;
|
||||||
|
VALUE= 3;
|
||||||
|
}
|
||||||
|
// result is logical comparison operation for this comparison.
|
||||||
|
CompareResult result = 1;
|
||||||
|
// target is the key-value field to inspect for the comparison.
|
||||||
|
CompareTarget target = 2;
|
||||||
|
// key is the subject key for the comparison operation.
|
||||||
|
bytes key = 3;
|
||||||
|
oneof target_union {
|
||||||
|
// version is the version of the given key
|
||||||
|
int64 version = 4;
|
||||||
|
// create_revision is the creation revision of the given key
|
||||||
|
int64 create_revision = 5;
|
||||||
|
// mod_revision is the last modified revision of the given key.
|
||||||
|
int64 mod_revision = 6;
|
||||||
|
// value is the value of the given key, in bytes.
|
||||||
|
bytes value = 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From google paxosdb paper:
|
||||||
|
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
|
||||||
|
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
|
||||||
|
// and consists of three components:
|
||||||
|
// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
|
||||||
|
// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
|
||||||
|
// may apply to the same or different entries in the database. All tests in the guard are applied and
|
||||||
|
// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
|
||||||
|
// it executes f op (see item 3 below).
|
||||||
|
// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
|
||||||
|
// lookup operation, and applies to a single database entry. Two different operations in the list may apply
|
||||||
|
// to the same or different entries in the database. These operations are executed
|
||||||
|
// if guard evaluates to
|
||||||
|
// true.
|
||||||
|
// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
|
||||||
|
message TxnRequest {
|
||||||
|
// compare is a list of predicates representing a conjunction of terms.
|
||||||
|
// If the comparisons succeed, then the success requests will be processed in order,
|
||||||
|
// and the response will contain their respective responses in order.
|
||||||
|
// If the comparisons fail, then the failure requests will be processed in order,
|
||||||
|
// and the response will contain their respective responses in order.
|
||||||
|
repeated Compare compare = 1;
|
||||||
|
// success is a list of requests which will be applied when compare evaluates to true.
|
||||||
|
repeated RequestOp success = 2;
|
||||||
|
// failure is a list of requests which will be applied when compare evaluates to false.
|
||||||
|
repeated RequestOp failure = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TxnResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// succeeded is set to true if the compare evaluated to true or false otherwise.
|
||||||
|
bool succeeded = 2;
|
||||||
|
// responses is a list of responses corresponding to the results from applying
|
||||||
|
// success if succeeded is true or failure if succeeded is false.
|
||||||
|
repeated ResponseOp responses = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactionRequest compacts the key-value store up to a given revision. All superseded keys
|
||||||
|
// with a revision less than the compaction revision will be removed.
|
||||||
|
message CompactionRequest {
|
||||||
|
// revision is the key-value store revision for the compaction operation.
|
||||||
|
int64 revision = 1;
|
||||||
|
// physical is set so the RPC will wait until the compaction is physically
|
||||||
|
// applied to the local database such that compacted entries are totally
|
||||||
|
// removed from the backend database.
|
||||||
|
bool physical = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CompactionResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HashRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message HashResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// hash is the hash value computed from the responding member's key-value store.
|
||||||
|
uint32 hash = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SnapshotRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message SnapshotResponse {
|
||||||
|
// header has the current key-value store information. The first header in the snapshot
|
||||||
|
// stream indicates the point in time of the snapshot.
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
|
||||||
|
// remaining_bytes is the number of blob bytes to be sent after this message
|
||||||
|
uint64 remaining_bytes = 2;
|
||||||
|
|
||||||
|
// blob contains the next chunk of the snapshot in the snapshot stream.
|
||||||
|
bytes blob = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchRequest {
|
||||||
|
// request_union is a request to either create a new watcher or cancel an existing watcher.
|
||||||
|
oneof request_union {
|
||||||
|
WatchCreateRequest create_request = 1;
|
||||||
|
WatchCancelRequest cancel_request = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchCreateRequest {
|
||||||
|
// key is the key to register for watching.
|
||||||
|
bytes key = 1;
|
||||||
|
// range_end is the end of the range [key, range_end) to watch. If range_end is not given,
|
||||||
|
// only the key argument is watched. If range_end is equal to '\0', all keys greater than
|
||||||
|
// or equal to the key argument are watched.
|
||||||
|
bytes range_end = 2;
|
||||||
|
// start_revision is an optional revision to watch from (inclusive). No start_revision is "now".
|
||||||
|
int64 start_revision = 3;
|
||||||
|
// progress_notify is set so that the etcd server will periodically send a WatchResponse with
|
||||||
|
// no events to the new watcher if there are no recent events. It is useful when clients
|
||||||
|
// wish to recover a disconnected watcher starting from a recent known revision.
|
||||||
|
// The etcd server may decide how often it will send notifications based on current load.
|
||||||
|
bool progress_notify = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchCancelRequest {
|
||||||
|
// watch_id is the watcher id to cancel so that no more events are transmitted.
|
||||||
|
int64 watch_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// watch_id is the ID of the watcher that corresponds to the response.
|
||||||
|
int64 watch_id = 2;
|
||||||
|
// created is set to true if the response is for a create watch request.
|
||||||
|
// The client should record the watch_id and expect to receive events for
|
||||||
|
// the created watcher from the same stream.
|
||||||
|
// All events sent to the created watcher will attach with the same watch_id.
|
||||||
|
bool created = 3;
|
||||||
|
// canceled is set to true if the response is for a cancel watch request.
|
||||||
|
// No further events will be sent to the canceled watcher.
|
||||||
|
bool canceled = 4;
|
||||||
|
// compact_revision is set to the minimum index if a watcher tries to watch
|
||||||
|
// at a compacted index.
|
||||||
|
//
|
||||||
|
// This happens when creating a watcher at a compacted revision or the watcher cannot
|
||||||
|
// catch up with the progress of the key-value store.
|
||||||
|
//
|
||||||
|
// The client should treat the watcher as canceled and should not try to create any
|
||||||
|
// watcher with the same start_revision again.
|
||||||
|
int64 compact_revision = 5;
|
||||||
|
|
||||||
|
repeated mvccpb.Event events = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseGrantRequest {
|
||||||
|
// TTL is the advisory time-to-live in seconds.
|
||||||
|
int64 TTL = 1;
|
||||||
|
// ID is the requested ID for the lease. If ID is set to 0, the lessor chooses an ID.
|
||||||
|
int64 ID = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseGrantResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// ID is the lease ID for the granted lease.
|
||||||
|
int64 ID = 2;
|
||||||
|
// TTL is the server chosen lease time-to-live in seconds.
|
||||||
|
int64 TTL = 3;
|
||||||
|
string error = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseRevokeRequest {
|
||||||
|
// ID is the lease ID to revoke. When the ID is revoked, all associated keys will be deleted.
|
||||||
|
int64 ID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseRevokeResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseKeepAliveRequest {
|
||||||
|
// ID is the lease ID for the lease to keep alive.
|
||||||
|
int64 ID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LeaseKeepAliveResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// ID is the lease ID from the keep alive request.
|
||||||
|
int64 ID = 2;
|
||||||
|
// TTL is the new time-to-live for the lease.
|
||||||
|
int64 TTL = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Member {
|
||||||
|
// ID is the member ID for this member.
|
||||||
|
uint64 ID = 1;
|
||||||
|
// name is the human-readable name of the member. If the member is not started, the name will be an empty string.
|
||||||
|
string name = 2;
|
||||||
|
// peerURLs is the list of URLs the member exposes to the cluster for communication.
|
||||||
|
repeated string peerURLs = 3;
|
||||||
|
// clientURLs is the list of URLs the member exposes to clients for communication. If the member is not started, clientURLs will be empty.
|
||||||
|
repeated string clientURLs = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberAddRequest {
|
||||||
|
// peerURLs is the list of URLs the added member will use to communicate with the cluster.
|
||||||
|
repeated string peerURLs = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberAddResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// member is the member information for the added member.
|
||||||
|
Member member = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberRemoveRequest {
|
||||||
|
// ID is the member ID of the member to remove.
|
||||||
|
uint64 ID = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberRemoveResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberUpdateRequest {
|
||||||
|
// ID is the member ID of the member to update.
|
||||||
|
uint64 ID = 1;
|
||||||
|
// peerURLs is the new list of URLs the member will use to communicate with the cluster.
|
||||||
|
repeated string peerURLs = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberUpdateResponse{
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberListRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemberListResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// members is a list of all members associated with the cluster.
|
||||||
|
repeated Member members = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DefragmentRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message DefragmentResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AlarmType {
|
||||||
|
NONE = 0; // default, used to query if any alarm is active
|
||||||
|
NOSPACE = 1; // space quota is exhausted
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlarmRequest {
|
||||||
|
enum AlarmAction {
|
||||||
|
GET = 0;
|
||||||
|
ACTIVATE = 1;
|
||||||
|
DEACTIVATE = 2;
|
||||||
|
}
|
||||||
|
// action is the kind of alarm request to issue. The action
|
||||||
|
// may GET alarm statuses, ACTIVATE an alarm, or DEACTIVATE a
|
||||||
|
// raised alarm.
|
||||||
|
AlarmAction action = 1;
|
||||||
|
// memberID is the ID of the member associated with the alarm. If memberID is 0, the
|
||||||
|
// alarm request covers all members.
|
||||||
|
uint64 memberID = 2;
|
||||||
|
// alarm is the type of alarm to consider for this request.
|
||||||
|
AlarmType alarm = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlarmMember {
|
||||||
|
// memberID is the ID of the member associated with the raised alarm.
|
||||||
|
uint64 memberID = 1;
|
||||||
|
// alarm is the type of alarm which has been raised.
|
||||||
|
AlarmType alarm = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlarmResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// alarms is a list of alarms associated with the alarm request.
|
||||||
|
repeated AlarmMember alarms = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message StatusResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// version is the cluster protocol version used by the responding member.
|
||||||
|
string version = 2;
|
||||||
|
// dbSize is the size of the backend database, in bytes, of the responding member.
|
||||||
|
int64 dbSize = 3;
|
||||||
|
// leader is the member ID which the responding member believes is the current leader.
|
||||||
|
uint64 leader = 4;
|
||||||
|
// raftIndex is the current raft index of the responding member.
|
||||||
|
uint64 raftIndex = 5;
|
||||||
|
// raftTerm is the current raft term of the responding member.
|
||||||
|
uint64 raftTerm = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthEnableRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthDisableRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthenticateRequest {
|
||||||
|
string name = 1;
|
||||||
|
string password = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserAddRequest {
|
||||||
|
string name = 1;
|
||||||
|
string password = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserGetRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserDeleteRequest {
|
||||||
|
// name is the name of the user to delete.
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserChangePasswordRequest {
|
||||||
|
// name is the name of the user whose password is being changed.
|
||||||
|
string name = 1;
|
||||||
|
// password is the new password for the user.
|
||||||
|
string password = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserGrantRoleRequest {
|
||||||
|
// user is the name of the user which should be granted a given role.
|
||||||
|
string user = 1;
|
||||||
|
// role is the name of the role to grant to the user.
|
||||||
|
string role = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserRevokeRoleRequest {
|
||||||
|
string name = 1;
|
||||||
|
string role = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleAddRequest {
|
||||||
|
// name is the name of the role to add to the authentication system.
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleGetRequest {
|
||||||
|
string role = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserListRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleListRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleDeleteRequest {
|
||||||
|
string role = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleGrantPermissionRequest {
|
||||||
|
// name is the name of the role which will be granted the permission.
|
||||||
|
string name = 1;
|
||||||
|
// perm is the permission to grant to the role.
|
||||||
|
authpb.Permission perm = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleRevokePermissionRequest {
|
||||||
|
string role = 1;
|
||||||
|
string key = 2;
|
||||||
|
string range_end = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthEnableResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthDisableResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthenticateResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
// token is an authorized token that can be used in succeeding RPCs
|
||||||
|
string token = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserAddResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserGetResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
|
||||||
|
repeated string roles = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserDeleteResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserChangePasswordResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserGrantRoleResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserRevokeRoleResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleAddResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleGetResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
|
||||||
|
repeated authpb.Permission perm = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleListResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
|
||||||
|
repeated string roles = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthUserListResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
|
||||||
|
repeated string users = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleDeleteResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleGrantPermissionResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AuthRoleRevokePermissionResponse {
|
||||||
|
ResponseHeader header = 1;
|
||||||
|
}
|
681
vendor/github.com/coreos/etcd/mvcc/mvccpb/kv.pb.go
generated
vendored
Normal file
681
vendor/github.com/coreos/etcd/mvcc/mvccpb/kv.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,681 @@
|
||||||
|
// Code generated by protoc-gen-gogo.
|
||||||
|
// source: kv.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package mvccpb is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
kv.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
KeyValue
|
||||||
|
Event
|
||||||
|
*/
|
||||||
|
package mvccpb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
import io "io"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
const _ = proto.ProtoPackageIsVersion1
|
||||||
|
|
||||||
|
type Event_EventType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PUT Event_EventType = 0
|
||||||
|
DELETE Event_EventType = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var Event_EventType_name = map[int32]string{
|
||||||
|
0: "PUT",
|
||||||
|
1: "DELETE",
|
||||||
|
}
|
||||||
|
var Event_EventType_value = map[string]int32{
|
||||||
|
"PUT": 0,
|
||||||
|
"DELETE": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Event_EventType) String() string {
|
||||||
|
return proto.EnumName(Event_EventType_name, int32(x))
|
||||||
|
}
|
||||||
|
func (Event_EventType) EnumDescriptor() ([]byte, []int) { return fileDescriptorKv, []int{1, 0} }
|
||||||
|
|
||||||
|
type KeyValue struct {
|
||||||
|
// key is the key in bytes. An empty key is not allowed.
|
||||||
|
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
// create_revision is the revision of last creation on this key.
|
||||||
|
CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`
|
||||||
|
// mod_revision is the revision of last modification on this key.
|
||||||
|
ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`
|
||||||
|
// version is the version of the key. A deletion resets
|
||||||
|
// the version to zero and any modification of the key
|
||||||
|
// increases its version.
|
||||||
|
Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
|
// value is the value held by the key, in bytes.
|
||||||
|
Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
|
||||||
|
// lease is the ID of the lease that attached to key.
|
||||||
|
// When the attached lease expires, the key will be deleted.
|
||||||
|
// If lease is 0, then no lease is attached to the key.
|
||||||
|
Lease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyValue) Reset() { *m = KeyValue{} }
|
||||||
|
func (m *KeyValue) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*KeyValue) ProtoMessage() {}
|
||||||
|
func (*KeyValue) Descriptor() ([]byte, []int) { return fileDescriptorKv, []int{0} }
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
// type is the kind of event. If type is a PUT, it indicates
|
||||||
|
// new data has been stored to the key. If type is a DELETE,
|
||||||
|
// it indicates the key was deleted.
|
||||||
|
Type Event_EventType `protobuf:"varint,1,opt,name=type,proto3,enum=mvccpb.Event_EventType" json:"type,omitempty"`
|
||||||
|
// kv holds the KeyValue for the event.
|
||||||
|
// A PUT event contains current kv pair.
|
||||||
|
// A PUT event with kv.Version=1 indicates the creation of a key.
|
||||||
|
// A DELETE/EXPIRE event contains the deleted key with
|
||||||
|
// its modification revision set to the revision of deletion.
|
||||||
|
Kv *KeyValue `protobuf:"bytes,2,opt,name=kv" json:"kv,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Event) Reset() { *m = Event{} }
|
||||||
|
func (m *Event) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Event) ProtoMessage() {}
|
||||||
|
func (*Event) Descriptor() ([]byte, []int) { return fileDescriptorKv, []int{1} }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*KeyValue)(nil), "mvccpb.KeyValue")
|
||||||
|
proto.RegisterType((*Event)(nil), "mvccpb.Event")
|
||||||
|
proto.RegisterEnum("mvccpb.Event_EventType", Event_EventType_name, Event_EventType_value)
|
||||||
|
}
|
||||||
|
func (m *KeyValue) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyValue) MarshalTo(data []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Key) > 0 {
|
||||||
|
data[i] = 0xa
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(len(m.Key)))
|
||||||
|
i += copy(data[i:], m.Key)
|
||||||
|
}
|
||||||
|
if m.CreateRevision != 0 {
|
||||||
|
data[i] = 0x10
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.CreateRevision))
|
||||||
|
}
|
||||||
|
if m.ModRevision != 0 {
|
||||||
|
data[i] = 0x18
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.ModRevision))
|
||||||
|
}
|
||||||
|
if m.Version != 0 {
|
||||||
|
data[i] = 0x20
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.Version))
|
||||||
|
}
|
||||||
|
if len(m.Value) > 0 {
|
||||||
|
data[i] = 0x2a
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(len(m.Value)))
|
||||||
|
i += copy(data[i:], m.Value)
|
||||||
|
}
|
||||||
|
if m.Lease != 0 {
|
||||||
|
data[i] = 0x30
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.Lease))
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Event) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Event) MarshalTo(data []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Type != 0 {
|
||||||
|
data[i] = 0x8
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.Type))
|
||||||
|
}
|
||||||
|
if m.Kv != nil {
|
||||||
|
data[i] = 0x12
|
||||||
|
i++
|
||||||
|
i = encodeVarintKv(data, i, uint64(m.Kv.Size()))
|
||||||
|
n1, err := m.Kv.MarshalTo(data[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n1
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFixed64Kv(data []byte, offset int, v uint64) int {
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
data[offset+1] = uint8(v >> 8)
|
||||||
|
data[offset+2] = uint8(v >> 16)
|
||||||
|
data[offset+3] = uint8(v >> 24)
|
||||||
|
data[offset+4] = uint8(v >> 32)
|
||||||
|
data[offset+5] = uint8(v >> 40)
|
||||||
|
data[offset+6] = uint8(v >> 48)
|
||||||
|
data[offset+7] = uint8(v >> 56)
|
||||||
|
return offset + 8
|
||||||
|
}
|
||||||
|
func encodeFixed32Kv(data []byte, offset int, v uint32) int {
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
data[offset+1] = uint8(v >> 8)
|
||||||
|
data[offset+2] = uint8(v >> 16)
|
||||||
|
data[offset+3] = uint8(v >> 24)
|
||||||
|
return offset + 4
|
||||||
|
}
|
||||||
|
func encodeVarintKv(data []byte, offset int, v uint64) int {
|
||||||
|
for v >= 1<<7 {
|
||||||
|
data[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
return offset + 1
|
||||||
|
}
|
||||||
|
func (m *KeyValue) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.Key)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovKv(uint64(l))
|
||||||
|
}
|
||||||
|
if m.CreateRevision != 0 {
|
||||||
|
n += 1 + sovKv(uint64(m.CreateRevision))
|
||||||
|
}
|
||||||
|
if m.ModRevision != 0 {
|
||||||
|
n += 1 + sovKv(uint64(m.ModRevision))
|
||||||
|
}
|
||||||
|
if m.Version != 0 {
|
||||||
|
n += 1 + sovKv(uint64(m.Version))
|
||||||
|
}
|
||||||
|
l = len(m.Value)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovKv(uint64(l))
|
||||||
|
}
|
||||||
|
if m.Lease != 0 {
|
||||||
|
n += 1 + sovKv(uint64(m.Lease))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Event) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.Type != 0 {
|
||||||
|
n += 1 + sovKv(uint64(m.Type))
|
||||||
|
}
|
||||||
|
if m.Kv != nil {
|
||||||
|
l = m.Kv.Size()
|
||||||
|
n += 1 + l + sovKv(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovKv(x uint64) (n int) {
|
||||||
|
for {
|
||||||
|
n++
|
||||||
|
x >>= 7
|
||||||
|
if x == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
func sozKv(x uint64) (n int) {
|
||||||
|
return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (m *KeyValue) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: KeyValue: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: KeyValue: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Key = append(m.Key[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Key == nil {
|
||||||
|
m.Key = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
|
||||||
|
}
|
||||||
|
m.CreateRevision = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.CreateRevision |= (int64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
|
||||||
|
}
|
||||||
|
m.ModRevision = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.ModRevision |= (int64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
|
||||||
|
}
|
||||||
|
m.Version = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Version |= (int64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Value = append(m.Value[:0], data[iNdEx:postIndex]...)
|
||||||
|
if m.Value == nil {
|
||||||
|
m.Value = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 6:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType)
|
||||||
|
}
|
||||||
|
m.Lease = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Lease |= (int64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipKv(data[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *Event) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: Event: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
|
||||||
|
}
|
||||||
|
m.Type = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.Type |= (Event_EventType(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.Kv == nil {
|
||||||
|
m.Kv = &KeyValue{}
|
||||||
|
}
|
||||||
|
if err := m.Kv.Unmarshal(data[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipKv(data[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipKv(data []byte) (n int, err error) {
|
||||||
|
l := len(data)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if data[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
return iNdEx, nil
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthKv
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 3:
|
||||||
|
for {
|
||||||
|
var innerWire uint64
|
||||||
|
var start int = iNdEx
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowKv
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
innerWire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerWireType := int(innerWire & 0x7)
|
||||||
|
if innerWireType == 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
next, err := skipKv(data[start:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iNdEx = start + next
|
||||||
|
}
|
||||||
|
return iNdEx, nil
|
||||||
|
case 4:
|
||||||
|
return iNdEx, nil
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
return iNdEx, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowKv = fmt.Errorf("proto: integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
var fileDescriptorKv = []byte{
|
||||||
|
// 285 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x90, 0x41, 0x4a, 0xc3, 0x40,
|
||||||
|
0x14, 0x86, 0x33, 0x4d, 0x9b, 0xd6, 0xd7, 0x52, 0xc3, 0x50, 0x70, 0x70, 0x31, 0xc4, 0x6e, 0x2c,
|
||||||
|
0x08, 0x11, 0xea, 0x0d, 0xc4, 0xac, 0x74, 0x21, 0x21, 0xba, 0x95, 0x34, 0x7d, 0x94, 0x92, 0xa6,
|
||||||
|
0x13, 0xd2, 0x38, 0x98, 0x9b, 0x78, 0x0a, 0xcf, 0xd1, 0x65, 0x8f, 0x60, 0xe3, 0x45, 0x24, 0x6f,
|
||||||
|
0x4c, 0xdd, 0x0c, 0xef, 0xff, 0xff, 0x6f, 0x98, 0xff, 0x0d, 0x0c, 0x52, 0xed, 0xe7, 0x85, 0x2a,
|
||||||
|
0x15, 0x77, 0x32, 0x9d, 0x24, 0xf9, 0xe2, 0x72, 0xb2, 0x52, 0x2b, 0x45, 0xd6, 0x6d, 0x33, 0x99,
|
||||||
|
0x74, 0xfa, 0xc5, 0x60, 0xf0, 0x88, 0xd5, 0x6b, 0xbc, 0x79, 0x47, 0xee, 0x82, 0x9d, 0x62, 0x25,
|
||||||
|
0x98, 0xc7, 0x66, 0xa3, 0xb0, 0x19, 0xf9, 0x35, 0x9c, 0x27, 0x05, 0xc6, 0x25, 0xbe, 0x15, 0xa8,
|
||||||
|
0xd7, 0xbb, 0xb5, 0xda, 0x8a, 0x8e, 0xc7, 0x66, 0x76, 0x38, 0x36, 0x76, 0xf8, 0xe7, 0xf2, 0x2b,
|
||||||
|
0x18, 0x65, 0x6a, 0xf9, 0x4f, 0xd9, 0x44, 0x0d, 0x33, 0xb5, 0x3c, 0x21, 0x02, 0xfa, 0x1a, 0x0b,
|
||||||
|
0x4a, 0xbb, 0x94, 0xb6, 0x92, 0x4f, 0xa0, 0xa7, 0x9b, 0x02, 0xa2, 0x47, 0x2f, 0x1b, 0xd1, 0xb8,
|
||||||
|
0x1b, 0x8c, 0x77, 0x28, 0x1c, 0xa2, 0x8d, 0x98, 0x7e, 0x40, 0x2f, 0xd0, 0xb8, 0x2d, 0xf9, 0x0d,
|
||||||
|
0x74, 0xcb, 0x2a, 0x47, 0x6a, 0x3b, 0x9e, 0x5f, 0xf8, 0x66, 0x4d, 0x9f, 0x42, 0x73, 0x46, 0x55,
|
||||||
|
0x8e, 0x21, 0x41, 0xdc, 0x83, 0x4e, 0xaa, 0xa9, 0xfa, 0x70, 0xee, 0xb6, 0x68, 0xbb, 0x77, 0xd8,
|
||||||
|
0x49, 0xf5, 0xd4, 0x83, 0xb3, 0xd3, 0x25, 0xde, 0x07, 0xfb, 0xf9, 0x25, 0x72, 0x2d, 0x0e, 0xe0,
|
||||||
|
0x3c, 0x04, 0x4f, 0x41, 0x14, 0xb8, 0xec, 0x5e, 0xec, 0x8f, 0xd2, 0x3a, 0x1c, 0xa5, 0xb5, 0xaf,
|
||||||
|
0x25, 0x3b, 0xd4, 0x92, 0x7d, 0xd7, 0x92, 0x7d, 0xfe, 0x48, 0x6b, 0xe1, 0xd0, 0x5f, 0xde, 0xfd,
|
||||||
|
0x06, 0x00, 0x00, 0xff, 0xff, 0xd6, 0x21, 0x8f, 0x2c, 0x75, 0x01, 0x00, 0x00,
|
||||||
|
}
|
46
vendor/github.com/coreos/etcd/mvcc/mvccpb/kv.proto
generated
vendored
Normal file
46
vendor/github.com/coreos/etcd/mvcc/mvccpb/kv.proto
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
package mvccpb;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
option (gogoproto.marshaler_all) = true;
|
||||||
|
option (gogoproto.sizer_all) = true;
|
||||||
|
option (gogoproto.unmarshaler_all) = true;
|
||||||
|
option (gogoproto.goproto_getters_all) = false;
|
||||||
|
option (gogoproto.goproto_enum_prefix_all) = false;
|
||||||
|
|
||||||
|
message KeyValue {
|
||||||
|
// key is the key in bytes. An empty key is not allowed.
|
||||||
|
bytes key = 1;
|
||||||
|
// create_revision is the revision of last creation on this key.
|
||||||
|
int64 create_revision = 2;
|
||||||
|
// mod_revision is the revision of last modification on this key.
|
||||||
|
int64 mod_revision = 3;
|
||||||
|
// version is the version of the key. A deletion resets
|
||||||
|
// the version to zero and any modification of the key
|
||||||
|
// increases its version.
|
||||||
|
int64 version = 4;
|
||||||
|
// value is the value held by the key, in bytes.
|
||||||
|
bytes value = 5;
|
||||||
|
// lease is the ID of the lease that attached to key.
|
||||||
|
// When the attached lease expires, the key will be deleted.
|
||||||
|
// If lease is 0, then no lease is attached to the key.
|
||||||
|
int64 lease = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Event {
|
||||||
|
enum EventType {
|
||||||
|
PUT = 0;
|
||||||
|
DELETE = 1;
|
||||||
|
}
|
||||||
|
// type is the kind of event. If type is a PUT, it indicates
|
||||||
|
// new data has been stored to the key. If type is a DELETE,
|
||||||
|
// it indicates the key was deleted.
|
||||||
|
EventType type = 1;
|
||||||
|
// kv holds the KeyValue for the event.
|
||||||
|
// A PUT event contains current kv pair.
|
||||||
|
// A PUT event with kv.Version=1 indicates the creation of a key.
|
||||||
|
// A DELETE/EXPIRE event contains the deleted key with
|
||||||
|
// its modification revision set to the revision of deletion.
|
||||||
|
KeyValue kv = 2;
|
||||||
|
}
|
98
vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go
generated
vendored
Normal file
98
vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// 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 implements utility functions related to files and paths.
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PrivateFileMode grants owner to read/write a file.
|
||||||
|
PrivateFileMode = 0600
|
||||||
|
// PrivateDirMode grants owner to make/remove files inside the directory.
|
||||||
|
PrivateDirMode = 0700
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "fileutil")
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 := path.Join(dir, ".touch")
|
||||||
|
if err := ioutil.WriteFile(f, []byte(""), PrivateFileMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Remove(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadDir returns the filenames in the given directory in sorted order.
|
||||||
|
func ReadDir(dirpath string) ([]string, error) {
|
||||||
|
dir, err := os.Open(dirpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
names, err := dir.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(dir string) error {
|
||||||
|
// If path is already a directory, MkdirAll does nothing
|
||||||
|
// and returns nil.
|
||||||
|
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(dir string) error {
|
||||||
|
err := TouchDirAll(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exist(name string) bool {
|
||||||
|
_, err := os.Stat(name)
|
||||||
|
return err == nil
|
||||||
|
}
|
26
vendor/github.com/coreos/etcd/pkg/fileutil/lock.go
generated
vendored
Normal file
26
vendor/github.com/coreos/etcd/pkg/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 }
|
49
vendor/github.com/coreos/etcd/pkg/fileutil/lock_flock.go
generated
vendored
Normal file
49
vendor/github.com/coreos/etcd/pkg/fileutil/lock_flock.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !windows,!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
|
||||||
|
}
|
96
vendor/github.com/coreos/etcd/pkg/fileutil/lock_linux.go
generated
vendored
Normal file
96
vendor/github.com/coreos/etcd/pkg/fileutil/lock_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// constants from /usr/include/bits/fcntl-linux.h
|
||||||
|
const (
|
||||||
|
F_OFD_GETLK = 37
|
||||||
|
F_OFD_SETLK = 37
|
||||||
|
F_OFD_SETLKW = 38
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
wrlck = syscall.Flock_t{
|
||||||
|
Type: syscall.F_WRLCK,
|
||||||
|
Whence: int16(os.SEEK_SET),
|
||||||
|
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, 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, err
|
||||||
|
}
|
||||||
|
|
||||||
|
flock := wrlck
|
||||||
|
if err = syscall.FcntlFlock(f.Fd(), 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, err
|
||||||
|
}
|
||||||
|
|
||||||
|
flock := wrlck
|
||||||
|
err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &LockedFile{f}, err
|
||||||
|
}
|
45
vendor/github.com/coreos/etcd/pkg/fileutil/lock_plan9.go
generated
vendored
Normal file
45
vendor/github.com/coreos/etcd/pkg/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)
|
||||||
|
}
|
||||||
|
}
|
62
vendor/github.com/coreos/etcd/pkg/fileutil/lock_solaris.go
generated
vendored
Normal file
62
vendor/github.com/coreos/etcd/pkg/fileutil/lock_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +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
|
||||||
|
}
|
29
vendor/github.com/coreos/etcd/pkg/fileutil/lock_unix.go
generated
vendored
Normal file
29
vendor/github.com/coreos/etcd/pkg/fileutil/lock_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +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)
|
||||||
|
}
|
125
vendor/github.com/coreos/etcd/pkg/fileutil/lock_windows.go
generated
vendored
Normal file
125
vendor/github.com/coreos/etcd/pkg/fileutil/lock_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +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
|
||||||
|
}
|
47
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate.go
generated
vendored
Normal file
47
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
// 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 extendFile {
|
||||||
|
return preallocExtend(f, sizeInBytes)
|
||||||
|
}
|
||||||
|
return preallocFixed(f, sizeInBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func preallocExtendTrunc(f *os.File, sizeInBytes int64) error {
|
||||||
|
curOff, err := f.Seek(0, os.SEEK_CUR)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size, err := f.Seek(sizeInBytes, os.SEEK_END)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = f.Seek(curOff, os.SEEK_SET); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sizeInBytes > size {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.Truncate(sizeInBytes)
|
||||||
|
}
|
43
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_darwin.go
generated
vendored
Normal file
43
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
fstore := &syscall.Fstore_t{
|
||||||
|
Flags: syscall.F_ALLOCATEALL,
|
||||||
|
Posmode: syscall.F_PEOFPOSMODE,
|
||||||
|
Length: sizeInBytes}
|
||||||
|
p := unsafe.Pointer(fstore)
|
||||||
|
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_PREALLOCATE), uintptr(p))
|
||||||
|
if errno == 0 || errno == syscall.ENOTSUP {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errno
|
||||||
|
}
|
49
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_unix.go
generated
vendored
Normal file
49
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build 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
|
||||||
|
}
|
25
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_unsupported.go
generated
vendored
Normal file
25
vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +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 }
|
78
vendor/github.com/coreos/etcd/pkg/fileutil/purge.go
generated
vendored
Normal file
78
vendor/github.com/coreos/etcd/pkg/fileutil/purge.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// 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"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
|
||||||
|
return purgeFile(dirname, suffix, max, interval, stop, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil.
|
||||||
|
func purgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string) <-chan error {
|
||||||
|
errC := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
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 := path.Join(dirname, newfnames[0])
|
||||||
|
l, err := TryLockFile(f, os.O_WRONLY, PrivateFileMode)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err = os.Remove(f); err != nil {
|
||||||
|
errC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = l.Close(); err != nil {
|
||||||
|
plog.Errorf("error unlocking %s when purging file (%v)", l.Name(), err)
|
||||||
|
errC <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plog.Infof("purged file %s successfully", 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
|
||||||
|
}
|
29
vendor/github.com/coreos/etcd/pkg/fileutil/sync.go
generated
vendored
Normal file
29
vendor/github.com/coreos/etcd/pkg/fileutil/sync.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !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()
|
||||||
|
}
|
40
vendor/github.com/coreos/etcd/pkg/fileutil/sync_darwin.go
generated
vendored
Normal file
40
vendor/github.com/coreos/etcd/pkg/fileutil/sync_darwin.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.
|
||||||
|
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package fileutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, f.Fd(), uintptr(syscall.F_FULLFSYNC), uintptr(0))
|
||||||
|
if errno == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fdatasync on darwin platform invokes fcntl(F_FULLFSYNC) for actual persistence
|
||||||
|
// on physical drive media.
|
||||||
|
func Fdatasync(f *os.File) error {
|
||||||
|
return Fsync(f)
|
||||||
|
}
|
34
vendor/github.com/coreos/etcd/pkg/fileutil/sync_linux.go
generated
vendored
Normal file
34
vendor/github.com/coreos/etcd/pkg/fileutil/sync_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2016 The etcd Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build 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()))
|
||||||
|
}
|
31
vendor/github.com/coreos/etcd/pkg/pathutil/path.go
generated
vendored
Normal file
31
vendor/github.com/coreos/etcd/pkg/pathutil/path.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package pathutil implements utility functions for handling slash-separated
|
||||||
|
// paths.
|
||||||
|
package pathutil
|
||||||
|
|
||||||
|
import "path"
|
||||||
|
|
||||||
|
// CanonicalURLPath returns the canonical url path for p, which follows the rules:
|
||||||
|
// 1. the path always starts with "/"
|
||||||
|
// 2. replace multiple slashes with a single slash
|
||||||
|
// 3. replace each '.' '..' path name element with equivalent one
|
||||||
|
// 4. keep the trailing slash
|
||||||
|
// The function is borrowed from stdlib http.cleanPath in server.go.
|
||||||
|
func CanonicalURLPath(p string) string {
|
||||||
|
if p == "" {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
if p[0] != '/' {
|
||||||
|
p = "/" + p
|
||||||
|
}
|
||||||
|
np := path.Clean(p)
|
||||||
|
// path.Clean removes trailing slash except for root,
|
||||||
|
// put the trailing slash back if necessary.
|
||||||
|
if p[len(p)-1] == '/' && np != "/" {
|
||||||
|
np += "/"
|
||||||
|
}
|
||||||
|
return np
|
||||||
|
}
|
16
vendor/github.com/coreos/etcd/pkg/tlsutil/doc.go
generated
vendored
Normal file
16
vendor/github.com/coreos/etcd/pkg/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
|
72
vendor/github.com/coreos/etcd/pkg/tlsutil/tlsutil.go
generated
vendored
Normal file
72
vendor/github.com/coreos/etcd/pkg/tlsutil/tlsutil.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// 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
|
||||||
|
}
|
17
vendor/github.com/coreos/etcd/pkg/transport/doc.go
generated
vendored
Normal file
17
vendor/github.com/coreos/etcd/pkg/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
|
94
vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener.go
generated
vendored
Normal file
94
vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type keepAliveConn interface {
|
||||||
|
SetKeepAlive(bool) error
|
||||||
|
SetKeepAlivePeriod(d time.Duration) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func NewKeepAliveListener(l net.Listener, scheme string, tlscfg *tls.Config) (net.Listener, error) {
|
||||||
|
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(l, tlscfg), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &keepaliveListener{
|
||||||
|
Listener: l,
|
||||||
|
}, 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 := c.(keepAliveConn)
|
||||||
|
// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
|
||||||
|
// default on linux: 30 + 8 * 30
|
||||||
|
// default on osx: 30 + 8 * 75
|
||||||
|
kac.SetKeepAlive(true)
|
||||||
|
kac.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
kac := c.(keepAliveConn)
|
||||||
|
// detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
|
||||||
|
// default on linux: 30 + 8 * 30
|
||||||
|
// default on osx: 30 + 8 * 75
|
||||||
|
kac.SetKeepAlive(true)
|
||||||
|
kac.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
c = tls.Server(c, l.config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
80
vendor/github.com/coreos/etcd/pkg/transport/limit_listen.go
generated
vendored
Normal file
80
vendor/github.com/coreos/etcd/pkg/transport/limit_listen.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *limitListenerConn) SetKeepAlive(doKeepAlive bool) error {
|
||||||
|
tcpc, ok := l.Conn.(*net.TCPConn)
|
||||||
|
if !ok {
|
||||||
|
return ErrNotTCP
|
||||||
|
}
|
||||||
|
return tcpc.SetKeepAlive(doKeepAlive)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *limitListenerConn) SetKeepAlivePeriod(d time.Duration) error {
|
||||||
|
tcpc, ok := l.Conn.(*net.TCPConn)
|
||||||
|
if !ok {
|
||||||
|
return ErrNotTCP
|
||||||
|
}
|
||||||
|
return tcpc.SetKeepAlivePeriod(d)
|
||||||
|
}
|
268
vendor/github.com/coreos/etcd/pkg/transport/listener.go
generated
vendored
Normal file
268
vendor/github.com/coreos/etcd/pkg/transport/listener.go
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
// 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/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/pkg/fileutil"
|
||||||
|
"github.com/coreos/etcd/pkg/tlsutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewListener(addr string, scheme string, tlscfg *tls.Config) (l net.Listener, err error) {
|
||||||
|
if scheme == "unix" || scheme == "unixs" {
|
||||||
|
// unix sockets via unix://laddr
|
||||||
|
l, err = NewUnixListener(addr)
|
||||||
|
} else {
|
||||||
|
l, err = net.Listen("tcp", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme == "https" || scheme == "unixs" {
|
||||||
|
if tlscfg == nil {
|
||||||
|
return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
l = tls.NewListener(l, tlscfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSInfo struct {
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
CAFile string
|
||||||
|
TrustedCAFile string
|
||||||
|
ClientCertAuth bool
|
||||||
|
|
||||||
|
// ServerName ensures the cert matches the given host in case of discovery / virtual hosting
|
||||||
|
ServerName string
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info TLSInfo) String() string {
|
||||||
|
return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info TLSInfo) Empty() bool {
|
||||||
|
return info.CertFile == "" && info.KeyFile == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelfCert(dirpath string, hosts []string) (info TLSInfo, err error) {
|
||||||
|
if err = fileutil.TouchDirAll(dirpath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certPath := path.Join(dirpath, "cert.pem")
|
||||||
|
keyPath := path.Join(dirpath, "key.pem")
|
||||||
|
_, errcert := os.Stat(certPath)
|
||||||
|
_, errkey := os.Stat(keyPath)
|
||||||
|
if errcert == nil && errkey == nil {
|
||||||
|
info.CertFile = certPath
|
||||||
|
info.KeyFile = keyPath
|
||||||
|
info.selfCert = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{Organization: []string{"etcd"}},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(365 * (24 * time.Hour)),
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range hosts {
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
|
||||||
|
} else {
|
||||||
|
tmpl.DNSNames = append(tmpl.DNSNames, strings.Split(host, ":")[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
certOut, err := os.Create(certPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
|
certOut.Close()
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
|
||||||
|
keyOut.Close()
|
||||||
|
|
||||||
|
return SelfCert(dirpath, hosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsCert, err := tlsutil.NewCert(info.CertFile, info.KeyFile, info.parseFunc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{*tlsCert},
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
ServerName: info.ServerName,
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cafiles returns a list of CA file paths.
|
||||||
|
func (info TLSInfo) cafiles() []string {
|
||||||
|
cs := make([]string, 0)
|
||||||
|
if info.CAFile != "" {
|
||||||
|
cs = append(cs, info.CAFile)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.ClientAuth = tls.NoClientCert
|
||||||
|
if info.CAFile != "" || info.ClientCertAuth {
|
||||||
|
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
|
}
|
||||||
|
|
||||||
|
CAFiles := info.cafiles()
|
||||||
|
if len(CAFiles) > 0 {
|
||||||
|
cp, err := tlsutil.NewCertPool(CAFiles)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.ClientCAs = cp
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
}
|
||||||
|
|
||||||
|
CAFiles := info.cafiles()
|
||||||
|
if len(CAFiles) > 0 {
|
||||||
|
cfg.RootCAs, err = tlsutil.NewCertPool(CAFiles)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// if given a CA, trust any host with a cert signed by the CA
|
||||||
|
cfg.ServerName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.selfCert {
|
||||||
|
cfg.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShallowCopyTLSConfig copies *tls.Config. This is only
|
||||||
|
// work-around for go-vet tests, which complains
|
||||||
|
//
|
||||||
|
// assignment copies lock value to p: crypto/tls.Config contains sync.Once contains sync.Mutex
|
||||||
|
//
|
||||||
|
// Keep up-to-date with 'go/src/crypto/tls/common.go'
|
||||||
|
func ShallowCopyTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
|
ncfg := tls.Config{
|
||||||
|
Time: cfg.Time,
|
||||||
|
Certificates: cfg.Certificates,
|
||||||
|
NameToCertificate: cfg.NameToCertificate,
|
||||||
|
GetCertificate: cfg.GetCertificate,
|
||||||
|
RootCAs: cfg.RootCAs,
|
||||||
|
NextProtos: cfg.NextProtos,
|
||||||
|
ServerName: cfg.ServerName,
|
||||||
|
ClientAuth: cfg.ClientAuth,
|
||||||
|
ClientCAs: cfg.ClientCAs,
|
||||||
|
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
||||||
|
CipherSuites: cfg.CipherSuites,
|
||||||
|
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
||||||
|
SessionTicketKey: cfg.SessionTicketKey,
|
||||||
|
ClientSessionCache: cfg.ClientSessionCache,
|
||||||
|
MinVersion: cfg.MinVersion,
|
||||||
|
MaxVersion: cfg.MaxVersion,
|
||||||
|
CurvePreferences: cfg.CurvePreferences,
|
||||||
|
}
|
||||||
|
return &ncfg
|
||||||
|
}
|
44
vendor/github.com/coreos/etcd/pkg/transport/timeout_conn.go
generated
vendored
Normal file
44
vendor/github.com/coreos/etcd/pkg/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
|
||||||
|
wtimeoutd time.Duration
|
||||||
|
rdtimeoutd time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c timeoutConn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.wtimeoutd > 0 {
|
||||||
|
if err := c.SetWriteDeadline(time.Now().Add(c.wtimeoutd)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c timeoutConn) Read(b []byte) (n int, err error) {
|
||||||
|
if c.rdtimeoutd > 0 {
|
||||||
|
if err := c.SetReadDeadline(time.Now().Add(c.rdtimeoutd)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
36
vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer.go
generated
vendored
Normal file
36
vendor/github.com/coreos/etcd/pkg/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{
|
||||||
|
rdtimeoutd: d.rdtimeoutd,
|
||||||
|
wtimeoutd: d.wtimeoutd,
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
return tconn, err
|
||||||
|
}
|
54
vendor/github.com/coreos/etcd/pkg/transport/timeout_listener.go
generated
vendored
Normal file
54
vendor/github.com/coreos/etcd/pkg/transport/timeout_listener.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 transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"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, tlscfg *tls.Config, rdtimeoutd, wtimeoutd time.Duration) (net.Listener, error) {
|
||||||
|
ln, err := NewListener(addr, scheme, tlscfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &rwTimeoutListener{
|
||||||
|
Listener: ln,
|
||||||
|
rdtimeoutd: rdtimeoutd,
|
||||||
|
wtimeoutd: wtimeoutd,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type rwTimeoutListener struct {
|
||||||
|
net.Listener
|
||||||
|
wtimeoutd time.Duration
|
||||||
|
rdtimeoutd time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rwln *rwTimeoutListener) Accept() (net.Conn, error) {
|
||||||
|
c, err := rwln.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return timeoutConn{
|
||||||
|
Conn: c,
|
||||||
|
wtimeoutd: rwln.wtimeoutd,
|
||||||
|
rdtimeoutd: rwln.rdtimeoutd,
|
||||||
|
}, nil
|
||||||
|
}
|
51
vendor/github.com/coreos/etcd/pkg/transport/timeout_transport.go
generated
vendored
Normal file
51
vendor/github.com/coreos/etcd/pkg/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 unncessary 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
|
||||||
|
}
|
49
vendor/github.com/coreos/etcd/pkg/transport/tls.go
generated
vendored
Normal file
49
vendor/github.com/coreos/etcd/pkg/transport/tls.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// 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 (
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
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.Dial("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
|
||||||
|
}
|
70
vendor/github.com/coreos/etcd/pkg/transport/transport.go
generated
vendored
Normal file
70
vendor/github.com/coreos/etcd/pkg/transport/transport.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// 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"
|
||||||
|
"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,
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: dialtimeoutd,
|
||||||
|
// value taken from http.DefaultTransport
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).Dial,
|
||||||
|
// value taken from http.DefaultTransport
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
TLSClientConfig: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer := (&net.Dialer{
|
||||||
|
Timeout: dialtimeoutd,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
})
|
||||||
|
dial := func(net, addr string) (net.Conn, error) {
|
||||||
|
return dialer.Dial("unix", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
tu := &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
Dial: dial,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
TLSClientConfig: cfg,
|
||||||
|
}
|
||||||
|
ut := &unixTransport{tu}
|
||||||
|
|
||||||
|
t.RegisterProtocol("unix", ut)
|
||||||
|
t.RegisterProtocol("unixs", ut)
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (urt *unixTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
req2 := *req
|
||||||
|
req2.URL.Scheme = strings.Replace(req.URL.Scheme, "unix", "http", 1)
|
||||||
|
return urt.Transport.RoundTrip(req)
|
||||||
|
}
|
40
vendor/github.com/coreos/etcd/pkg/transport/unix_listener.go
generated
vendored
Normal file
40
vendor/github.com/coreos/etcd/pkg/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.RemoveAll(addr); err != nil {
|
||||||
|
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.RemoveAll(ul.Addr().String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ul.Listener.Close()
|
||||||
|
}
|
17
vendor/github.com/coreos/etcd/pkg/types/doc.go
generated
vendored
Normal file
17
vendor/github.com/coreos/etcd/pkg/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
|
41
vendor/github.com/coreos/etcd/pkg/types/id.go
generated
vendored
Normal file
41
vendor/github.com/coreos/etcd/pkg/types/id.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// 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] }
|
178
vendor/github.com/coreos/etcd/pkg/types/set.go
generated
vendored
Normal file
178
vendor/github.com/coreos/etcd/pkg/types/set.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
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()
|
||||||
|
usResult := ts.us.Sub(other).(*unsafeSet)
|
||||||
|
return &tsafeSet{usResult, sync.RWMutex{}}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue